This commit is contained in:
M
2020-06-01 21:56:21 +03:00
298 changed files with 9437 additions and 5461 deletions
+651 -99
View File
File diff suppressed because it is too large Load Diff
+13 -1
View File
@@ -21,6 +21,8 @@ import 'package:cake_wallet/src/stores/login/login_store.dart';
import 'package:cake_wallet/src/stores/balance/balance_store.dart';
import 'package:cake_wallet/src/stores/sync/sync_store.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/stores/send_template/send_template_store.dart';
import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
import 'package:cake_wallet/src/screens/root/root.dart';
import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
@@ -32,6 +34,8 @@ import 'package:cake_wallet/src/domain/common/default_settings_migration.dart';
import 'package:cake_wallet/src/domain/common/fiat_currency.dart';
import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:cake_wallet/src/domain/common/template.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
import 'package:cake_wallet/src/domain/services/wallet_service.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/language.dart';
@@ -49,6 +53,8 @@ void main() async {
Hive.registerAdapter(TradeAdapter());
Hive.registerAdapter(WalletInfoAdapter());
Hive.registerAdapter(WalletTypeAdapter());
Hive.registerAdapter(TemplateAdapter());
Hive.registerAdapter(ExchangeTemplateAdapter());
final secureStorage = FlutterSecureStorage();
final transactionDescriptionsBoxKey = await getEncryptionKey(
@@ -66,6 +72,8 @@ void main() async {
final trades =
await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
final walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName);
final templates = await Hive.openBox<Template>(Template.boxName);
final exchangeTemplates = await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
final sharedPreferences = await SharedPreferences.getInstance();
final walletService = WalletService();
@@ -102,6 +110,8 @@ void main() async {
final loginStore = LoginStore(
sharedPreferences: sharedPreferences, walletsService: walletListService);
final seedLanguageStore = SeedLanguageStore();
final sendTemplateStore = SendTemplateStore(templateSource: templates);
final exchangeTemplateStore = ExchangeTemplateStore(templateSource: exchangeTemplates);
setReactions(
settingsStore: settingsStore,
@@ -127,7 +137,9 @@ void main() async {
Provider(create: (_) => nodes),
Provider(create: (_) => transactionDescriptions),
Provider(create: (_) => trades),
Provider(create: (_) => seedLanguageStore)
Provider(create: (_) => seedLanguageStore),
Provider(create: (_) => sendTemplateStore),
Provider(create: (_) => exchangeTemplateStore),
], child: CakeWalletApp()));
}
+21 -63
View File
@@ -1,72 +1,30 @@
import 'package:flutter/material.dart';
class Palette {
static const Color lightBlue = Color.fromRGBO(126, 147, 177, 1.0);
static const Color lightBlueWithOpacity = Color.fromRGBO(126, 147, 177, 0.4);
static const Color lightGrey = Color.fromRGBO(224, 233, 246, 1.0);
static const Color purple = Color.fromRGBO(243, 229, 245, 1.0);
static const Color deepPink = Color.fromRGBO(225, 190, 231, 1.0);
static const Color indigo = Color.fromRGBO(232, 234, 246, 1.0);
static const Color deepIndigo = Color.fromRGBO(197, 202, 233, 1.0);
static const Color creamyGrey = Color.fromRGBO(245, 246, 249, 1.0);
static const Color deepPurple = Color.fromRGBO(124, 83, 240, 1.0);
static const Color wildDarkBlue = Color.fromRGBO(155, 172, 197, 1.0);
static const Color wildDarkBlueWithOpacity = Color.fromRGBO(155, 172, 197, 0.1);
static const Color lightGreen = Color.fromRGBO(52, 186, 204, 1.0);
static const Color darkPurple = Color.fromRGBO(124, 83, 240, 1.0);
static const Color brightBlue = Color.fromRGBO(151, 226, 255, 0.6);
static const Color cloudySky = Color.fromRGBO(121, 201, 233, 0.8);
static const Color darkGrey = Color.fromRGBO(136, 155, 183, 0.21);
static const Color shadowGrey = Color.fromRGBO(132, 141, 198, 0.14);
static const Color shadowGreyWithOpacity = Color.fromRGBO(132, 141, 198, 0.05);
static const Color blueGrey = Color.fromRGBO(103, 107, 141, 1.0);
static const Color lightGrey2 = Color.fromRGBO(249, 249, 249, 1.0);
static const Color violet = Color.fromRGBO(138, 80, 255, 1.0);
static const Color lightViolet = Color.fromRGBO(131, 87, 255, 1);
static const Color cakeGreen = Color.fromRGBO(41, 187, 244, 1.0);
static const Color cakeGreenWithOpacity = Color.fromRGBO(41, 187, 244, 0.2);
static const Color switchBackground = Color.fromRGBO(228, 231, 240, 1.0);
static const Color switchBorder = Color.fromRGBO(211, 222, 238, 1.0);
static const Color green = Color.fromRGBO(39, 206, 80, 1.0);
static const Color red = Color.fromRGBO(255, 51, 51, 1.0);
static const Color nightBlue = Color.fromRGBO(34, 40, 75, 1.0);
static const Color lavender = Color.fromRGBO(249, 250, 253, 1);
static const Color lightLavender = Color.fromRGBO(242, 244, 247, 1);
static const Color cadetBlue = Color.fromRGBO(191, 201, 215, 1);
static const Color manatee = Color.fromRGBO(138, 153, 175, 1);
static const Color separator = Color.fromRGBO(240, 241, 244, 1);
static const Color containerLavender = Color.fromRGBO(226, 235, 238, 0.4);
static const Color purpleBlue = Color.fromRGBO(84, 92, 139, 1);
static const Color floatingActionButton = Color.fromRGBO(213, 56, 99, 1);
static const Color failure = Color.fromRGBO(226, 35, 35, 1);
static const Color powered = Color.fromRGBO(191, 201, 215, 1);
static const Color buttonShadow = Color.fromRGBO(23, 46, 77, 0.129207);
static const Color blueAlice = Color.fromRGBO(231, 240, 253, 1.0);
static const Color lightBlue = Color.fromRGBO(172, 203, 238, 1.0);
static const Color lavender = Color.fromRGBO(237, 245, 252, 1.0);
static const Color oceanBlue = Color.fromRGBO(30, 52, 78, 1.0);
static const Color lightBlueGrey = Color.fromRGBO(118, 131, 169, 1.0);
static const Color periwinkle = Color.fromRGBO(197, 208, 230, 1.0);
static const Color blue = Color.fromRGBO(88, 143, 252, 1.0);
static const Color darkLavender = Color.fromRGBO(225, 238, 250, 1.0);
static const Color nightBlue = Color.fromRGBO(46, 57, 96, 1.0);
}
class PaletteDark {
static const Color darkThemeTitle = Color.fromRGBO(132, 154, 186, 1.0);
static const Color darkThemeTitleViolet = Color.fromRGBO(121, 92, 190, 1.0);
static const Color darkThemeGrey = Color.fromRGBO(100, 115, 137, 1.0);
static const Color darkThemeGreyWithOpacity = Color.fromRGBO(100, 115, 137, 0.5);
static const Color darkThemeMidGrey = Color.fromRGBO(20, 26, 38, 1.0);
static const Color darkThemePurpleButton = Color.fromRGBO(182, 143, 255, 0.1);
static const Color darkThemePurpleButtonBorder = Color.fromRGBO(161, 120, 255, 0.7);
static const Color darkThemeBackground = Color.fromRGBO(39, 41, 50, 1.0);
static const Color darkThemeBackgroundDark = Color.fromRGBO(9, 12, 18, 1.0);
static const Color darkThemeDarkGrey = Color.fromRGBO(218, 228, 243, 0.06);
static const Color darkGrey = Color.fromRGBO(218, 228, 243, 0.4);
static const Color darkThemeBlackWithOpacity = Color.fromRGBO(0, 0, 0, 0.2);
static const Color darkThemeBlack = Color.fromRGBO(7, 10, 14, 1.0);
static const Color darkThemeAppBarBlack = Color.fromRGBO(4, 5, 7, 1.0);
static const Color darkThemeViolet = Color.fromRGBO(131, 87, 255, 0.2);
static const Color darkThemeIndigoButton = Color.fromRGBO(216, 223, 246, 0.1);
static const Color darkThemeIndigoButtonBorder = Color.fromRGBO(196, 206, 237, 0.4);
static const Color darkThemeBlueButton = Color.fromRGBO(151, 226, 255, 0.1);
static const Color darkThemeBlueButtonBorder = Color.fromRGBO(62, 190, 240, 0.6);
static const Color darkThemeCloseButton = Color.fromRGBO(34, 40, 74, 1.0);
static const Color darkThemePinButton = Color.fromRGBO(136, 155, 183, 0.1);
static const Color darkThemePinDigitButton = Color.fromRGBO(100, 115, 137, 0.34);
static const Color switchBackground = Color.fromRGBO(100, 115, 137, 0.4);
static const Color wildDarkBlueWithOpacity = Color.fromRGBO(155, 172, 197, 0.4);
static const Color wildDarkBlue = Color.fromRGBO(155, 172, 197, 0.8);
static const Color distantBlue = Color.fromRGBO(70, 85, 133, 1.0); // mainBackgroundColor
static const Color lightDistantBlue = Color.fromRGBO(81, 96, 147, 1.0); // borderCardColor
static const Color nightBlue = Color.fromRGBO(45, 56, 95, 1.0); // walletCardBottomEndSync
static const Color gray = Color.fromRGBO(140, 153, 201, 1.0); // walletCardText
static const Color violetBlue = Color.fromRGBO(51, 63, 104, 1.0); // walletCardAddressField
static const Color moderateBlue = Color.fromRGBO(63, 77, 122, 1.0); // walletCardSubAddressField
static const Color darkNightBlue = Color.fromRGBO(33, 43, 73, 1.0); // historyPanel
static const Color pigeonBlue = Color.fromRGBO(91, 112, 146, 1.0); // historyPanelText
static const Color moderateNightBlue = Color.fromRGBO(39, 53, 96, 1.0); // historyPanelButton
static const Color headerNightBlue = Color.fromRGBO(41, 52, 84, 1.0); // menuHeader
static const Color lightNightBlue = Color.fromRGBO(48, 59, 95, 1.0); // menuList
static const Color moderatePurpleBlue = Color.fromRGBO(57, 74, 95, 1.0); // selectButtonText
}
+40 -25
View File
@@ -1,4 +1,3 @@
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
@@ -25,6 +24,7 @@ import 'package:cake_wallet/src/domain/monero/account.dart';
import 'package:cake_wallet/src/domain/common/mnemotic_item.dart';
import 'package:cake_wallet/src/domain/common/transaction_info.dart';
import 'package:cake_wallet/src/domain/monero/subaddress.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
// MARK: Import stores
@@ -69,7 +69,6 @@ import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
import 'package:cake_wallet/src/screens/seed_language/seed_language_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
import 'package:cake_wallet/src/screens/accounts/account_page.dart';
import 'package:cake_wallet/src/screens/accounts/account_list_page.dart';
import 'package:cake_wallet/src/screens/address_book/address_book_page.dart';
import 'package:cake_wallet/src/screens/address_book/contact_page.dart';
import 'package:cake_wallet/src/screens/show_keys/show_keys_page.dart';
@@ -88,6 +87,9 @@ import 'package:cake_wallet/src/screens/auth/create_login_page.dart';
import 'package:cake_wallet/src/screens/seed/create_seed_page.dart';
import 'package:cake_wallet/src/screens/dashboard/create_dashboard_page.dart';
import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
import 'package:cake_wallet/src/screens/send/send_template_page.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
class Router {
static Route<dynamic> generateRoute(
@@ -120,9 +122,8 @@ class Router {
secureStorage: FlutterSecureStorage(),
sharedPreferences: sharedPreferences)),
child: SetupPinCodePage(
onPinCodeSetup: (context, _) => Navigator.pushNamed(
context, Routes.newWallet,
arguments: type))));
onPinCodeSetup: (context, _) =>
Navigator.pushNamed(context, Routes.newWallet))));
case Routes.newWallet:
final type = settings.arguments as WalletType;
@@ -250,18 +251,24 @@ class Router {
transactionDescriptions: transactionDescriptions)),
], child: SendPage()));
case Routes.sendTemplate:
return CupertinoPageRoute<void>(
builder: (_) => Provider(
create: (_) => SendStore(
walletService: walletService,
priceStore: priceStore,
transactionDescriptions: transactionDescriptions),
child: SendTemplatePage())
);
case Routes.receive:
return CupertinoPageRoute<void>(
fullscreenDialog: true,
builder: (_) => MultiProvider(
providers: walletService.getType() == WalletType.monero
? [
Provider(
create: (_) => SubaddressListStore(
walletService: walletService))
]
: [],
child: ReceivePage()));
builder: (_) => MultiProvider(providers: [
Provider(
create: (_) =>
SubaddressListStore(walletService: walletService))
], child: ReceivePage()));
case Routes.transactionDetails:
return CupertinoPageRoute<void>(
@@ -343,17 +350,6 @@ class Router {
authenticationStore: authenticationStore);
});
case Routes.accountList:
return MaterialPageRoute<void>(
builder: (context) {
return MultiProvider(providers: [
Provider(
create: (_) =>
AccountListStore(walletService: walletService)),
], child: AccountListPage());
},
fullscreenDialog: true);
case Routes.accountCreation:
return CupertinoPageRoute<String>(builder: (context) {
return Provider(
@@ -486,6 +482,25 @@ class Router {
}),
], child: ExchangePage()));
case Routes.exchangeTemplate:
return MaterialPageRoute<void>(
builder: (_) => Provider(create: (_) {
final xmrtoprovider = XMRTOExchangeProvider();
return ExchangeStore(
initialProvider: xmrtoprovider,
initialDepositCurrency: CryptoCurrency.xmr,
initialReceiveCurrency: CryptoCurrency.btc,
trades: trades,
providerList: [
xmrtoprovider,
ChangeNowExchangeProvider(),
MorphTokenExchangeProvider(trades: trades)
],
walletStore: walletStore);
}, child: ExchangeTemplatePage(),)
);
case Routes.settings:
return MaterialPageRoute<void>(
builder: (_) => Provider(
+3 -1
View File
@@ -25,7 +25,6 @@ class Routes {
static const newNode = '/new_node_list';
static const login = '/login';
static const splash = '/splash';
static const accountList = '/account_list';
static const accountCreation = '/account_new';
static const addressBook = '/address_book';
static const pickerAddressBook = '/picker_address_book';
@@ -44,4 +43,7 @@ class Routes {
static const rescan = '/rescan';
static const faq = '/faq';
static const changeLanguage = '/change_language';
static const newWalletType = '/new_wallet_type';
static const sendTemplate = '/send_template';
static const exchangeTemplate = '/exchange_template';
}
+22
View File
@@ -0,0 +1,22 @@
import 'package:hive/hive.dart';
part 'template.g.dart';
@HiveType(typeId: 6)
class Template extends HiveObject {
Template({this.name, this.address, this.cryptoCurrency, this.amount});
static const boxName = 'Template';
@HiveField(0)
String name;
@HiveField(1)
String address;
@HiveField(2)
String cryptoCurrency;
@HiveField(3)
String amount;
}
@@ -0,0 +1,35 @@
import 'package:hive/hive.dart';
part 'exchange_template.g.dart';
@HiveType(typeId: 7)
class ExchangeTemplate extends HiveObject {
ExchangeTemplate({
this.amount,
this.depositCurrency,
this.receiveCurrency,
this.provider,
this.depositAddress,
this.receiveAddress
});
static const boxName = 'ExchangeTemplate';
@HiveField(0)
String amount;
@HiveField(1)
String depositCurrency;
@HiveField(2)
String receiveCurrency;
@HiveField(3)
String provider;
@HiveField(4)
String depositAddress;
@HiveField(5)
String receiveAddress;
}
+1 -1
View File
@@ -49,7 +49,7 @@ class SubaddressList {
{int accountIndex, int addressIndex, String label}) async {
await subaddress_list.setLabelForSubaddress(
accountIndex: accountIndex, addressIndex: addressIndex, label: label);
await update();
await update(accountIndex: accountIndex);
}
void refresh({int accountIndex}) {
+131 -98
View File
@@ -1,120 +1,153 @@
import 'dart:ui';
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/stores/account_list/account_list_store.dart';
import 'package:cake_wallet/src/screens/accounts/widgets/account_tile.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
class AccountListPage extends BasePage {
@override
String get title => S.current.accounts;
class AccountListPage extends StatefulWidget {
AccountListPage({@required this.accountListStore});
final AccountListStore accountListStore;
@override
Widget trailing(BuildContext context) {
final accountListStore = Provider.of<AccountListStore>(context);
AccountListPageForm createState() => AccountListPageForm(accountListStore);
}
return Container(
width: 28.0,
height: 28.0,
decoration: BoxDecoration(
shape: BoxShape.circle, color: Theme.of(context).selectedRowColor),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Icon(Icons.add, color: Palette.violet, size: 22.0),
ButtonTheme(
minWidth: 28.0,
height: 28.0,
child: FlatButton(
shape: CircleBorder(),
onPressed: () async {
await Navigator.of(context)
.pushNamed(Routes.accountCreation);
accountListStore.updateAccountList();
},
child: Offstage()),
)
],
));
}
class AccountListPageForm extends State<AccountListPage> {
AccountListPageForm(this.accountListStore);
final AccountListStore accountListStore;
@override
Widget body(BuildContext context) {
final accountListStore = Provider.of<AccountListStore>(context);
Widget build(BuildContext context) {
final walletStore = Provider.of<WalletStore>(context);
final currentColor = Theme.of(context).selectedRowColor;
final notCurrentColor = Theme.of(context).backgroundColor;
return Container(
padding: EdgeInsets.only(top: 10, bottom: 20),
child: Observer(builder: (_) {
final accounts = accountListStore.accounts;
return ListView.builder(
itemCount: accounts == null ? 0 : accounts.length,
itemBuilder: (BuildContext context, int index) {
final account = accounts[index];
return Observer(builder: (_) {
final isCurrent = walletStore.account.id == account.id;
return Slidable(
key: Key(account.id.toString()),
actionPane: SlidableDrawerActionPane(),
child: Container(
color: isCurrent ? currentColor : notCurrentColor,
child: Column(
children: <Widget>[
ListTile(
title: Text(
account.label,
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context)
.primaryTextTheme
.headline
.color),
),
onTap: () {
if (isCurrent) {
return;
}
walletStore.setAccount(account);
Navigator.of(context).pop();
},
),
Divider(
color: Theme.of(context).dividerTheme.color,
height: 1.0,
)
],
return GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
color: Colors.transparent,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: Text(
S.of(context).choose_account,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: Colors.white
),
),
),
secondaryActions: <Widget>[
IconSlideAction(
caption: S.of(context).edit,
color: Colors.blue,
icon: Icons.edit,
onTap: () async {
await Navigator.of(context).pushNamed(
Routes.accountCreation,
arguments: account);
// await accountListStore.updateAccountList().then((_) {
// if (isCurrent) walletStore.setAccount(accountListStore.accounts[index]);
// });
},
)
],
);
});
});
}),
Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
child: GestureDetector(
onTap: () => null,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(14)),
child: Container(
height: 296,
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: Column(
children: <Widget>[
Expanded(
child: Observer(
builder: (_) {
final accounts = accountListStore.accounts;
return ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Theme.of(context).dividerColor,
height: 1,
),
itemCount: accounts == null ? 0 : accounts.length,
itemBuilder: (context, index) {
final account = accounts[index];
return Observer(
builder: (_) {
final isCurrent = walletStore.account.id == account.id;
return AccountTile(
isCurrent: isCurrent,
accountName: account.label,
onTap: () {
if (isCurrent) {
return;
}
walletStore.setAccount(account);
Navigator.of(context).pop();
}
);
}
);
},
);
}
)
),
GestureDetector(
onTap: () async {
await Navigator.of(context)
.pushNamed(Routes.accountCreation);
accountListStore.updateAccountList();
},
child: Container(
height: 62,
color: Colors.white,
padding: EdgeInsets.only(left: 24, right: 24),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(
Icons.add,
color: PaletteDark.darkNightBlue,
),
Padding(
padding: EdgeInsets.only(left: 5),
child: Text(
S.of(context).create_new_account,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: PaletteDark.darkNightBlue,
decoration: TextDecoration.none,
),
),
)
],
),
),
),
)
],
),
),
),
),
)
],
),
),
),
),
),
);
}
}
+23 -26
View File
@@ -8,6 +8,7 @@ import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/domain/monero/account.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
class AccountPage extends BasePage {
AccountPage({this.account});
@@ -15,7 +16,7 @@ class AccountPage extends BasePage {
final Account account;
@override
String get title => 'Account';
String get title => S.current.account;
@override
Widget body(BuildContext context) => AccountForm(account);
@@ -50,30 +51,31 @@ class AccountFormState extends State<AccountForm> {
Widget build(BuildContext context) {
final accountListStore = Provider.of<AccountListStore>(context);
_textController.addListener(() {
if (_textController.text.isNotEmpty) {
accountListStore.setDisabledStatus(false);
} else {
accountListStore.setDisabledStatus(true);
}
});
return Form(
key: _formKey,
child: Container(
padding: EdgeInsets.all(20.0),
color: Theme.of(context).backgroundColor,
padding: EdgeInsets.all(24.0),
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: TextFormField(
decoration: InputDecoration(
hintStyle: TextStyle(color: Theme.of(context).hintColor),
child: BaseTextFormField(
controller: _textController,
hintText: S.of(context).account,
focusedBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor, width: 1.0))),
controller: _textController,
validator: (value) {
accountListStore.validateAccountName(value);
return accountListStore.errorMessage;
},
),
validator: (value) {
accountListStore.validateAccountName(value);
return accountListStore.errorMessage;
},
)
)),
Observer(
builder: (_) => LoadingPrimaryButton(
@@ -93,16 +95,11 @@ class AccountFormState extends State<AccountForm> {
Navigator.of(context).pop(_textController.text);
},
text:
widget.account != null ? 'Rename' : S.of(context).add,
color: Theme.of(context)
.primaryTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.primaryTextTheme
.button
.decorationColor,
widget.account != null ? S.of(context).rename : S.of(context).add,
color: Colors.green,
textColor: Colors.white,
isLoading: accountListStore.isAccountCreating,
isDisabled: accountListStore.isDisabledStatus,
))
],
),
@@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
class AccountTile extends StatelessWidget {
AccountTile({
@required this.isCurrent,
@required this.accountName,
@required this.onTap
});
final bool isCurrent;
final String accountName;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
final color = isCurrent ? Theme.of(context).accentTextTheme.subtitle.decorationColor : Colors.transparent;
final textColor = isCurrent ? Colors.blue : Theme.of(context).primaryTextTheme.title.color;
return GestureDetector(
onTap: onTap,
child: Container(
height: 77,
padding: EdgeInsets.only(left: 24, right: 24),
alignment: Alignment.centerLeft,
color: color,
child: Text(
accountName,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: textColor,
decoration: TextDecoration.none,
),
),
),
);
}
}
@@ -5,26 +5,20 @@ import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/stores/address_book/address_book_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
class AddressBookPage extends BasePage {
AddressBookPage({this.isEditable = true});
final bool isEditable;
@override
bool get isModalBackButton => true;
@override
String get title => S.current.address_book;
@override
AppBarStyle get appBarStyle => AppBarStyle.withShadow;
@override
Widget trailing(BuildContext context) {
if (!isEditable) {
@@ -34,17 +28,21 @@ class AddressBookPage extends BasePage {
final addressBookStore = Provider.of<AddressBookStore>(context);
return Container(
width: 28.0,
height: 28.0,
width: 32.0,
height: 32.0,
decoration: BoxDecoration(
shape: BoxShape.circle, color: Theme.of(context).selectedRowColor),
shape: BoxShape.circle,
color: Theme.of(context).accentTextTheme.title.backgroundColor
),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Icon(Icons.add, color: Palette.violet, size: 22.0),
Icon(Icons.add,
color: Theme.of(context).primaryTextTheme.title.color,
size: 22.0),
ButtonTheme(
minWidth: 28.0,
height: 28.0,
minWidth: 32.0,
height: 32.0,
child: FlatButton(
shape: CircleBorder(),
onPressed: () async {
@@ -63,20 +61,30 @@ class AddressBookPage extends BasePage {
final addressBookStore = Provider.of<AddressBookStore>(context);
return Container(
color: Theme.of(context).backgroundColor,
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Observer(
builder: (_) => ListView.separated(
separatorBuilder: (_, __) => Divider(
color: Theme.of(context).dividerTheme.color,
height: 1.0,
),
separatorBuilder: (_, __) => Container(
height: 1,
padding: EdgeInsets.only(left: 24),
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: Container(
height: 1,
color: Theme.of(context).dividerColor,
),
),
itemCount: addressBookStore.contactList == null
? 0
: addressBookStore.contactList.length,
itemBuilder: (BuildContext context, int index) {
final contact = addressBookStore.contactList[index];
final image = _getCurrencyImage(contact.type);
final content = ListTile(
final isDrawTop = index == 0 ? true : false;
final isDrawBottom = index == addressBookStore.contactList.length - 1 ? true : false;
final content = GestureDetector(
onTap: () async {
if (!isEditable) {
Navigator.of(context).pop(contact);
@@ -86,39 +94,69 @@ class AddressBookPage extends BasePage {
final isCopied = await showNameAndAddressDialog(
context, contact.name, contact.address);
if (isCopied) {
if (isCopied != null && isCopied) {
await Clipboard.setData(
ClipboardData(text: contact.address));
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Copied to Clipboard'),
content: Text(
S.of(context).copied_to_clipboard,
style: TextStyle(
color: Colors.white
),
),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 1500),
),
);
}
},
leading: Container(
height: 25.0,
width: 48.0,
alignment: Alignment.center,
decoration: BoxDecoration(
color: _getCurrencyBackgroundColor(contact.type),
borderRadius: BorderRadius.circular(6.0),
),
child: Text(
contact.type.toString(),
style: TextStyle(
fontSize: 11.0,
color: _getCurrencyTextColor(contact.type),
child: Column(
children: <Widget>[
isDrawTop
? Container(
width: double.infinity,
height: 1,
color: Theme.of(context).dividerColor,
)
: Offstage(),
Container(
width: double.infinity,
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: Padding(
padding: const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
image != null
? image
: Offstage(),
Padding(
padding: image != null
? EdgeInsets.only(left: 12)
: EdgeInsets.only(left: 0),
child: Text(
contact.name,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryTextTheme.title.color
),
),
)
],
)
),
),
),
),
title: Text(
contact.name,
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color),
isDrawBottom
? Container(
width: double.infinity,
height: 1,
color: Theme.of(context).dividerColor,
)
: Offstage(),
],
),
);
@@ -130,7 +168,7 @@ class AddressBookPage extends BasePage {
child: content,
secondaryActions: <Widget>[
IconSlideAction(
caption: 'Edit',
caption: S.of(context).edit,
color: Colors.blue,
icon: Icons.edit,
onTap: () async {
@@ -141,7 +179,7 @@ class AddressBookPage extends BasePage {
},
),
IconSlideAction(
caption: 'Delete',
caption: S.of(context).delete,
color: Colors.red,
icon: CupertinoIcons.delete,
onTap: () async {
@@ -171,95 +209,68 @@ class AddressBookPage extends BasePage {
));
}
Color _getCurrencyBackgroundColor(CryptoCurrency currency) {
Color color;
Image _getCurrencyImage(CryptoCurrency currency) {
Image image;
switch (currency) {
case CryptoCurrency.xmr:
color = Palette.cakeGreenWithOpacity;
image = Image.asset('assets/images/monero.png', height: 24, width: 24);
break;
case CryptoCurrency.ada:
color = Colors.blue[200];
image = Image.asset('assets/images/ada.png', height: 24, width: 24);
break;
case CryptoCurrency.bch:
color = Colors.orangeAccent;
image = Image.asset('assets/images/bch.png', height: 24, width: 24);
break;
case CryptoCurrency.bnb:
color = Colors.blue;
image = Image.asset('assets/images/bnb.png', height: 24, width: 24);
break;
case CryptoCurrency.btc:
color = Colors.orange;
image = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
break;
case CryptoCurrency.dash:
color = Colors.blue;
image = Image.asset('assets/images/dash.png', height: 24, width: 24);
break;
case CryptoCurrency.eos:
color = Colors.orangeAccent;
image = Image.asset('assets/images/eos.png', height: 24, width: 24);
break;
case CryptoCurrency.eth:
color = Colors.black;
image = Image.asset('assets/images/eth.png', height: 24, width: 24);
break;
case CryptoCurrency.ltc:
color = Colors.blue[200];
image = Image.asset('assets/images/litecoin.png', height: 24, width: 24);
break;
case CryptoCurrency.nano:
color = Colors.orange;
image = Image.asset('assets/images/nano.png', height: 24, width: 24);
break;
case CryptoCurrency.trx:
color = Colors.black;
image = Image.asset('assets/images/trx.png', height: 24, width: 24);
break;
case CryptoCurrency.usdt:
color = Colors.blue[200];
image = Image.asset('assets/images/usdt.png', height: 24, width: 24);
break;
case CryptoCurrency.xlm:
color = color = Colors.blue;
image = Image.asset('assets/images/xlm.png', height: 24, width: 24);
break;
case CryptoCurrency.xrp:
color = Colors.orangeAccent;
image = Image.asset('assets/images/xrp.png', height: 24, width: 24);
break;
default:
color = Colors.white;
image = null;
}
return color;
}
Color _getCurrencyTextColor(CryptoCurrency currency) {
Color color;
switch (currency) {
case CryptoCurrency.xmr:
color = Palette.cakeGreen;
break;
case CryptoCurrency.ltc:
case CryptoCurrency.ada:
case CryptoCurrency.usdt:
color = Palette.lightBlue;
break;
default:
color = Colors.white;
}
return color;
return image;
}
Future<bool> showAlertDialog(BuildContext context) async {
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text(
'Remove contact',
textAlign: TextAlign.center,
),
content: const Text(
'Are you sure that you want to remove selected contact?',
textAlign: TextAlign.center,
),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop( false),
child: const Text('Cancel')),
FlatButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Remove')),
],
return AlertWithTwoActions(
alertTitle: S.of(context).address_remove_contact,
alertContent: S.of(context).address_remove_content,
leftButtonText: S.of(context).remove,
rightButtonText: S.of(context).cancel,
actionLeftButton: () => Navigator.of(context).pop(true),
actionRightButton: () => Navigator.of(context).pop(false)
);
});
}
@@ -269,24 +280,13 @@ class AddressBookPage extends BasePage {
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
name,
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.bold),
),
content: Text(
address,
textAlign: TextAlign.center,
),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text('Cancel')),
FlatButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text('Copy'))
],
return AlertWithTwoActions(
alertTitle: name,
alertContent: address,
leftButtonText: S.of(context).copy,
rightButtonText: S.of(context).cancel,
actionLeftButton: () => Navigator.of(context).pop(true),
actionRightButton: () => Navigator.of(context).pop(false)
);
});
}
+161 -185
View File
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
@@ -9,7 +10,9 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/address_text_field.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
class ContactPage extends BasePage {
ContactPage({this.contact});
@@ -37,22 +40,27 @@ class ContactFormState extends State<ContactForm> {
final _contactNameController = TextEditingController();
final _currencyTypeController = TextEditingController();
final _addressController = TextEditingController();
final currencies = CryptoCurrency.all;
CryptoCurrency _selectectCrypto = CryptoCurrency.xmr;
CryptoCurrency _selectectCrypto;
@override
void initState() {
super.initState();
if (widget.contact == null) {
_currencyTypeController.text = _selectectCrypto.toString();
} else {
if (widget.contact != null) {
_selectectCrypto = widget.contact.type;
_contactNameController.text = widget.contact.name;
_currencyTypeController.text = _selectectCrypto.toString();
_addressController.text = widget.contact.address;
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
}
}
void afterLayout(dynamic _) {
final addressBookStore = Provider.of<AddressBookStore>(context);
addressBookStore.setDisabledStatus(false);
}
@override
void dispose() {
_contactNameController.dispose();
@@ -61,198 +69,166 @@ class ContactFormState extends State<ContactForm> {
super.dispose();
}
Future<void> _setCurrencyType(BuildContext context) async {
var currencyType = CryptoCurrency.all[0].toString();
var selectedCurrency = CryptoCurrency.all[0];
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).please_select),
backgroundColor: Theme.of(context).backgroundColor,
content: Container(
height: 150.0,
child: CupertinoPicker(
backgroundColor: Theme.of(context).backgroundColor,
itemExtent: 45.0,
onSelectedItemChanged: (int index) {
selectedCurrency = CryptoCurrency.all[index];
currencyType = CryptoCurrency.all[index].toString();
},
children:
List.generate(CryptoCurrency.all.length, (int index) {
return Center(
child: Text(
CryptoCurrency.all[index].toString(),
style: TextStyle(
color: Theme.of(context)
.primaryTextTheme
.caption
.color),
),
);
})),
),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(S.of(context).cancel)),
FlatButton(
onPressed: () {
_selectectCrypto = selectedCurrency;
_currencyTypeController.text = currencyType;
Navigator.of(context).pop();
},
child: Text(S.of(context).ok))
],
);
});
void onHandleControllers(AddressBookStore addressBookStore) {
if (_contactNameController.text.isNotEmpty &&
_addressController.text.isNotEmpty &&
_currencyTypeController.text.isNotEmpty) {
addressBookStore.setDisabledStatus(false);
} else {
addressBookStore.setDisabledStatus(true);
}
}
@override
Widget build(BuildContext context) {
final addressBookStore = Provider.of<AddressBookStore>(context);
final downArrow = Image.asset(
'assets/images/arrow_bottom_purple_icon.png',
color: Theme.of(context).dividerColor,
height: 8);
return ScrollableWithBottomSection(
content: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextFormField(
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.headline.color),
decoration: InputDecoration(
hintStyle: TextStyle(color: Theme.of(context).hintColor),
hintText: S.of(context).contact_name,
focusedBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor, width: 1.0))),
controller: _contactNameController,
validator: (value) {
addressBookStore.validateContactName(value);
return addressBookStore.errorMessage;
},
),
SizedBox(height: 14.0),
Container(
child: InkWell(
onTap: () => _setCurrencyType(context),
child: IgnorePointer(
child: TextFormField(
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context)
.primaryTextTheme
.headline
.color),
decoration: InputDecoration(
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
width: 1.0))),
controller: _currencyTypeController,
_contactNameController.addListener(() {onHandleControllers(addressBookStore);});
_currencyTypeController.addListener(() {onHandleControllers(addressBookStore);});
_addressController.addListener(() {onHandleControllers(addressBookStore);});
return Container(
color: Theme.of(context).backgroundColor,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.all(24),
content: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
BaseTextFormField(
controller: _contactNameController,
hintText: S.of(context).contact_name,
validator: (value) {
addressBookStore.validateContactName(value);
return addressBookStore.errorMessage;
}
),
Padding(
padding: EdgeInsets.only(top: 20),
child: Container(
child: InkWell(
onTap: () => _presentPicker(context),
child: IgnorePointer(
child: BaseTextFormField(
controller: _currencyTypeController,
hintText: S.of(context).settings_currency,
suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
downArrow
],
),
)
),
),
),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: AddressTextField(
controller: _addressController,
options: [AddressTextFieldOption.qrCode],
validator: (value) {
addressBookStore.validateAddress(value,
cryptoCurrency: _selectectCrypto);
return addressBookStore.errorMessage;
},
),
)
],
),
),
bottomSectionPadding: EdgeInsets.only(
left: 24,
right: 24,
bottom: 24
),
bottomSection: Row(
children: <Widget>[
Expanded(
child: PrimaryButton(
onPressed: () {
setState(() {
_selectectCrypto = null;
_contactNameController.text = '';
_currencyTypeController.text = '';
_addressController.text = '';
});
},
text: S.of(context).reset,
color: Colors.red,
textColor: Colors.white
),
),
SizedBox(height: 14.0),
AddressTextField(
controller: _addressController,
options: [AddressTextFieldOption.qrCode],
validator: (value) {
addressBookStore.validateAddress(value,
cryptoCurrency: _selectectCrypto);
return addressBookStore.errorMessage;
},
SizedBox(width: 20),
Expanded(
child: Observer(
builder: (_) => PrimaryButton(
onPressed: () async {
if (!_formKey.currentState.validate()) {
return;
}
try {
if (widget.contact == null) {
final newContact = Contact(
name: _contactNameController.text,
address: _addressController.text,
type: _selectectCrypto);
await addressBookStore.add(contact: newContact);
} else {
widget.contact.name = _contactNameController.text;
widget.contact.address = _addressController.text;
widget.contact
.updateCryptoCurrency(currency: _selectectCrypto);
await addressBookStore.update(
contact: widget.contact);
}
Navigator.pop(context);
} catch (e) {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.current.contact,
alertContent: e.toString(),
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
}
},
text: S.of(context).save,
color: Colors.green,
textColor: Colors.white,
isDisabled: addressBookStore.isDisabledStatus,
)
)
)
],
),
)),
);
}
void _presentPicker(BuildContext context) {
showDialog<void>(
builder: (_) => CurrencyPicker(
selectedAtIndex: currencies.indexOf(_selectectCrypto),
items: currencies,
title: S.of(context).please_select,
onItemSelected: (CryptoCurrency item) {
_selectectCrypto = item;
_currencyTypeController.text = _selectectCrypto.toString();
}
),
bottomSection: Row(
children: <Widget>[
Expanded(
child: PrimaryButton(
onPressed: () {
setState(() {
_selectectCrypto = CryptoCurrency.xmr;
_contactNameController.text = '';
_currencyTypeController.text =
_selectectCrypto.toString();
_addressController.text = '';
});
},
text: S.of(context).reset,
color:
Theme.of(context).accentTextTheme.button.backgroundColor,
borderColor:
Theme.of(context).accentTextTheme.button.decorationColor),
),
SizedBox(width: 20),
Expanded(
child: PrimaryButton(
onPressed: () async {
if (!_formKey.currentState.validate()) {
return;
}
try {
if (widget.contact == null) {
final newContact = Contact(
name: _contactNameController.text,
address: _addressController.text,
type: _selectectCrypto);
await addressBookStore.add(contact: newContact);
} else {
widget.contact.name = _contactNameController.text;
widget.contact.address = _addressController.text;
widget.contact
.updateCryptoCurrency(currency: _selectectCrypto);
await addressBookStore.update(
contact: widget.contact);
}
Navigator.pop(context);
} catch (e) {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
e.toString(),
textAlign: TextAlign.center,
),
actions: <Widget>[
FlatButton(
onPressed: () =>
Navigator.of(context).pop(),
child: Text(S.of(context).ok))
],
);
});
}
},
text: S.of(context).save,
color: Theme.of(context)
.primaryTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.primaryTextTheme
.button
.decorationColor))
],
));
context: context);
}
}
+17 -1
View File
@@ -24,6 +24,8 @@ class AuthPage extends StatefulWidget {
class AuthPageState extends State<AuthPage> {
final _key = GlobalKey<ScaffoldState>();
final _pinCodeKey = GlobalKey<PinCodeState>();
final _backArrowImageDarkTheme =
Image.asset('assets/images/back_arrow_dark_theme.png');
void changeProcessText(String text) {
_key.currentState.showSnackBar(
@@ -121,7 +123,21 @@ class AuthPageState extends State<AuthPage> {
return Scaffold(
key: _key,
appBar: CupertinoNavigationBar(
leading: widget.closable ? CloseButton() : Container(),
leading: widget.closable
? SizedBox(
height: 37,
width: 20,
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: () => Navigator.of(context).pop(),
child: _backArrowImageDarkTheme),
),
)
: Container(),
backgroundColor: Theme.of(context).backgroundColor,
border: null,
),
+14 -11
View File
@@ -4,13 +4,15 @@ import 'package:cake_wallet/src/widgets/nav_bar.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/themes.dart';
import 'package:cake_wallet/theme_changer.dart';
import 'package:cake_wallet/palette.dart';
enum AppBarStyle { regular, withShadow }
abstract class BasePage extends StatelessWidget {
String get title => null;
bool get isModalBackButton => false;
Color get backgroundColor => Colors.white;
Color get backgroundLightColor => Colors.white;
Color get backgroundDarkColor => PaletteDark.darkNightBlue;
bool get resizeToAvoidBottomPadding => true;
AppBarStyle get appBarStyle => AppBarStyle.regular;
@@ -60,8 +62,8 @@ abstract class BasePage extends StatelessWidget {
: Text(
title,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w600,
fontSize: 18.0,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color),
);
}
@@ -82,8 +84,8 @@ abstract class BasePage extends StatelessWidget {
middle: middle(context),
trailing: trailing(context),
backgroundColor: _isDarkTheme
? Theme.of(context).backgroundColor
: backgroundColor);
? backgroundDarkColor
: backgroundLightColor);
case AppBarStyle.withShadow:
return NavBar.withShadow(
@@ -92,8 +94,8 @@ abstract class BasePage extends StatelessWidget {
middle: middle(context),
trailing: trailing(context),
backgroundColor: _isDarkTheme
? Theme.of(context).backgroundColor
: backgroundColor);
? backgroundDarkColor
: backgroundLightColor);
default:
return NavBar(
@@ -102,8 +104,8 @@ abstract class BasePage extends StatelessWidget {
middle: middle(context),
trailing: trailing(context),
backgroundColor: _isDarkTheme
? Theme.of(context).backgroundColor
: backgroundColor);
? backgroundDarkColor
: backgroundLightColor);
}
}
@@ -115,8 +117,9 @@ abstract class BasePage extends StatelessWidget {
final _isDarkTheme = _themeChanger.getTheme() == Themes.darkTheme;
return Scaffold(
backgroundColor:
_isDarkTheme ? Theme.of(context).backgroundColor : backgroundColor,
backgroundColor: _isDarkTheme
? backgroundDarkColor
: backgroundLightColor,
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
appBar: appBar(context),
body: SafeArea(child: body(context)),
+58 -576
View File
@@ -1,106 +1,14 @@
import 'package:provider/provider.dart';
import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker;
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
import 'package:cake_wallet/src/domain/common/sync_status.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/src/stores/action_list/action_list_store.dart';
import 'package:cake_wallet/src/stores/balance/balance_store.dart';
import 'package:cake_wallet/src/stores/sync/sync_store.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/stores/action_list/date_section_item.dart';
import 'package:cake_wallet/src/stores/action_list/trade_list_item.dart';
import 'package:cake_wallet/src/stores/action_list/transaction_list_item.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/dashboard/date_section_raw.dart';
import 'package:cake_wallet/src/screens/dashboard/trade_row.dart';
import 'package:cake_wallet/src/screens/dashboard/transaction_raw.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/wallet_card.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/trade_history_panel.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/menu_widget.dart';
class DashboardPage extends BasePage {
class DashboardPage extends StatelessWidget {
final _bodyKey = GlobalKey();
@override
Widget leading(BuildContext context) {
return SizedBox(
width: 30,
child: FlatButton(
padding: EdgeInsets.all(0),
onPressed: () => _presentWalletMenu(context),
child: Image.asset('assets/images/more.png',
color: Theme.of(context).primaryTextTheme.caption.color,
width: 30)));
}
@override
Widget middle(BuildContext context) {
final walletStore = Provider.of<WalletStore>(context);
return Observer(builder: (_) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
walletStore.name,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.title.color),
),
SizedBox(height: 5),
Text(
walletStore.account != null ? '${walletStore.account.label}' : '',
style: TextStyle(
fontWeight: FontWeight.w400,
fontSize: 10,
color: Theme.of(context).primaryTextTheme.title.color),
),
]);
});
}
@override
Widget trailing(BuildContext context) {
return SizedBox(
width: 20,
child: FlatButton(
padding: EdgeInsets.all(0),
onPressed: () => Navigator.of(context).pushNamed(Routes.settings),
child: Image.asset('assets/images/settings_icon.png',
color: Colors.grey, height: 20)),
);
}
@override
Widget body(BuildContext context) => DashboardPageBody(key: _bodyKey);
@override
Widget floatingActionButton(BuildContext context) => FloatingActionButton(
child: Image.asset('assets/images/exchange_icon.png',
color: Colors.white, height: 26, width: 22),
backgroundColor: Palette.floatingActionButton,
onPressed: () async => await Navigator.of(context, rootNavigator: true)
.pushNamed(Routes.exchange));
void _presentWalletMenu(BuildContext bodyContext) {
final walletMenu = WalletMenu(bodyContext);
showDialog<void>(
builder: (_) => Picker(
items: walletMenu.items,
selectedAtIndex: -1,
title: S.of(bodyContext).wallet_menu,
pickerHeight: 510,
onItemSelected: (String item) =>
walletMenu.action(walletMenu.items.indexOf(item))),
context: bodyContext);
}
Widget build(BuildContext context) => DashboardPageBody(key: _bodyKey);
}
class DashboardPageBody extends StatefulWidget {
@@ -111,488 +19,62 @@ class DashboardPageBody extends StatefulWidget {
}
class DashboardPageBodyState extends State<DashboardPageBody> {
final _connectionStatusObserverKey = GlobalKey();
final _balanceObserverKey = GlobalKey();
final _balanceTitleObserverKey = GlobalKey();
final _syncingObserverKey = GlobalKey();
final _listObserverKey = GlobalKey();
final _listKey = GlobalKey();
@override
Widget build(BuildContext context) {
final balanceStore = Provider.of<BalanceStore>(context);
final actionListStore = Provider.of<ActionListStore>(context);
final syncStore = Provider.of<SyncStore>(context);
final settingsStore = Provider.of<SettingsStore>(context);
final transactionDateFormat = settingsStore.getCurrentDateFormat(
formatUSA: "MMMM d, yyyy, HH:mm",
formatDefault: "d MMMM yyyy, HH:mm");
final menuButton = Image.asset('assets/images/header.png',
color: Theme.of(context).primaryTextTheme.title.color,
);
return Observer(
key: _listObserverKey,
builder: (_) {
final items = actionListStore.items == null
? <String>[]
: actionListStore.items;
final itemsCount = items.length + 2;
return ListView.builder(
key: _listKey,
padding: EdgeInsets.only(bottom: 15),
itemCount: itemsCount,
itemBuilder: (context, index) {
if (index == 0) {
return Container(
margin: EdgeInsets.only(bottom: 20),
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor,
boxShadow: [
BoxShadow(
color: Palette.shadowGreyWithOpacity,
blurRadius: 10,
offset: Offset(0, 12))
]),
child: Column(
children: <Widget>[
Observer(
key: _syncingObserverKey,
builder: (_) {
final status = syncStore.status;
final statusText = status.title();
final progress = syncStore.status.progress();
final isFialure = status is FailedSyncStatus;
var descriptionText = '';
if (status is SyncingSyncStatus) {
descriptionText = S
.of(context)
.Blocks_remaining(
syncStore.status.toString());
}
if (status is FailedSyncStatus) {
descriptionText = S
.of(context)
.please_try_to_connect_to_another_node;
}
return Container(
child: Column(
children: [
SizedBox(
height: 3,
child: LinearProgressIndicator(
backgroundColor: Palette.separator,
valueColor:
AlwaysStoppedAnimation<Color>(
Palette.cakeGreen),
value: progress,
),
),
SizedBox(height: 10),
Text(statusText,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: isFialure
? Palette.failure
: Palette.cakeGreen)),
Text(descriptionText,
style: TextStyle(
fontSize: 11,
color: Palette.wildDarkBlue,
height: 2.0))
],
),
);
}),
GestureDetector(
onTapUp: (_) => balanceStore.isReversing = false,
onTapDown: (_) => balanceStore.isReversing = true,
child: Container(
padding: EdgeInsets.only(top: 40, bottom: 40),
color: Colors.transparent,
child: Column(
children: <Widget>[
Container(width: double.infinity),
Observer(
key: _balanceTitleObserverKey,
builder: (_) {
final savedDisplayMode =
settingsStore.balanceDisplayMode;
final displayMode = balanceStore
.isReversing
? (savedDisplayMode ==
BalanceDisplayMode
.availableBalance
? BalanceDisplayMode.fullBalance
: BalanceDisplayMode
.availableBalance)
: savedDisplayMode;
return Text(displayMode.toString(),
style: TextStyle(
color: Palette.violet,
fontSize: 16));
}),
Observer(
key: _connectionStatusObserverKey,
builder: (_) {
final savedDisplayMode =
settingsStore.balanceDisplayMode;
var balance = '---';
final displayMode = balanceStore
.isReversing
? (savedDisplayMode ==
BalanceDisplayMode
.availableBalance
? BalanceDisplayMode.fullBalance
: BalanceDisplayMode
.availableBalance)
: savedDisplayMode;
if (displayMode ==
BalanceDisplayMode.availableBalance) {
balance =
balanceStore.unlockedBalance ??
'0.0';
}
if (displayMode ==
BalanceDisplayMode.fullBalance) {
balance =
balanceStore.fullBalance ?? '0.0';
}
return Text(
balance,
style: TextStyle(
color: Theme.of(context)
.primaryTextTheme
.caption
.color,
fontSize: 42),
);
}),
Padding(
padding: EdgeInsets.only(top: 7),
child: Observer(
key: _balanceObserverKey,
builder: (_) {
final savedDisplayMode =
settingsStore.balanceDisplayMode;
final displayMode =
balanceStore.isReversing
? (savedDisplayMode ==
BalanceDisplayMode
.availableBalance
? BalanceDisplayMode
.fullBalance
: BalanceDisplayMode
.availableBalance)
: savedDisplayMode;
final symbol = settingsStore
.fiatCurrency
.toString();
var balance = '---';
if (displayMode ==
BalanceDisplayMode
.availableBalance) {
balance =
'${balanceStore.fiatUnlockedBalance} $symbol';
}
if (displayMode ==
BalanceDisplayMode.fullBalance) {
balance =
'${balanceStore.fiatFullBalance} $symbol';
}
return Text(balance,
style: TextStyle(
color: Palette.wildDarkBlue,
fontSize: 16));
}))
],
),
),
),
Padding(
padding: const EdgeInsets.only(
left: 20, right: 20, bottom: 30),
child: Container(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Expanded(
child: PrimaryImageButton(
image: Image.asset(
'assets/images/send_icon.png',
height: 25,
width: 25),
text: S.of(context).send,
onPressed: () => Navigator.of(context,
rootNavigator: true)
.pushNamed(Routes.send),
color: Theme.of(context)
.primaryTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.primaryTextTheme
.button
.decorationColor,
)),
SizedBox(width: 10),
Expanded(
child: PrimaryImageButton(
image: Image.asset(
'assets/images/receive_icon.png',
height: 25,
width: 25),
text: S.of(context).receive,
onPressed: () => Navigator.of(context,
rootNavigator: true)
.pushNamed(Routes.receive),
color: Theme.of(context)
.accentTextTheme
.caption
.backgroundColor,
borderColor: Theme.of(context)
.accentTextTheme
.caption
.decorationColor,
))
],
),
)),
],
),
);
}
if (index == 1 && actionListStore.totalCount > 0) {
return Padding(
padding: EdgeInsets.only(right: 20, top: 10, bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
PopupMenuButton<int>(
itemBuilder: (context) => [
PopupMenuItem(
enabled: false,
value: -1,
child: Text(S.of(context).transactions,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.caption.color))),
PopupMenuItem(
value: 0,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(S.of(context).incoming),
Checkbox(
value: actionListStore
.transactionFilterStore
.displayIncoming,
onChanged: (value) =>
actionListStore
.transactionFilterStore
.toggleIncoming(),
)
]))),
PopupMenuItem(
value: 1,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(S.of(context).outgoing),
Checkbox(
value: actionListStore
.transactionFilterStore
.displayOutgoing,
onChanged: (value) =>
actionListStore
.transactionFilterStore
.toggleOutgoing(),
)
]))),
PopupMenuItem(
value: 2,
child:
Text(S.of(context).transactions_by_date)),
PopupMenuDivider(),
PopupMenuItem(
enabled: false,
value: -1,
child: Text(S.of(context).trades,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.caption.color))),
PopupMenuItem(
value: 3,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text('XMR.TO'),
Checkbox(
value: actionListStore
.tradeFilterStore
.displayXMRTO,
onChanged: (value) =>
actionListStore
.tradeFilterStore
.toggleDisplayExchange(
ExchangeProviderDescription
.xmrto),
)
]))),
PopupMenuItem(
value: 4,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text('Change.NOW'),
Checkbox(
value: actionListStore
.tradeFilterStore
.displayChangeNow,
onChanged: (value) =>
actionListStore
.tradeFilterStore
.toggleDisplayExchange(
ExchangeProviderDescription
.changeNow),
)
]))),
PopupMenuItem(
value: 5,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text('MorphToken'),
Checkbox(
value: actionListStore
.tradeFilterStore
.displayMorphToken,
onChanged: (value) =>
actionListStore
.tradeFilterStore
.toggleDisplayExchange(
ExchangeProviderDescription
.morphToken),
)
])))
],
child: Text(S.of(context).filters,
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color)),
onSelected: (item) async {
if (item == 2) {
final List<DateTime> picked =
await date_rage_picker.showDatePicker(
context: context,
initialFirstDate: DateTime.now()
.subtract(Duration(days: 1)),
initialLastDate: (DateTime.now()),
firstDate: DateTime(2015),
lastDate: DateTime.now()
.add(Duration(days: 1)));
if (picked != null && picked.length == 2) {
actionListStore.transactionFilterStore
.changeStartDate(picked.first);
actionListStore.transactionFilterStore
.changeEndDate(picked.last);
}
}
},
)
]),
);
}
index -= 2;
if (index < 0 || index >= items.length) {
return Container();
}
final item = items[index];
if (item is DateSectionItem) {
return DateSectionRaw(date: item.date);
}
if (item is TransactionListItem) {
final transaction = item.transaction;
final savedDisplayMode = settingsStore.balanceDisplayMode;
final formattedAmount =
savedDisplayMode == BalanceDisplayMode.hiddenBalance
? '---'
: transaction.amountFormatted();
final formattedFiatAmount =
savedDisplayMode == BalanceDisplayMode.hiddenBalance
? '---'
: transaction.fiatAmount();
return TransactionRow(
onTap: () => Navigator.of(context).pushNamed(
Routes.transactionDetails,
arguments: transaction),
direction: transaction.direction,
formattedDate:
transactionDateFormat.format(transaction.date),
formattedAmount: formattedAmount,
formattedFiatAmount: formattedFiatAmount,
isPending: transaction.isPending);
}
if (item is TradeListItem) {
final trade = item.trade;
final savedDisplayMode = settingsStore.balanceDisplayMode;
final formattedAmount = trade.amount != null
? savedDisplayMode == BalanceDisplayMode.hiddenBalance
? '---'
: trade.amountFormatted()
: trade.amount;
return TradeRow(
onTap: () => Navigator.of(context)
.pushNamed(Routes.tradeDetails, arguments: trade),
provider: trade.provider,
from: trade.from,
to: trade.to,
createdAtFormattedDate:
transactionDateFormat.format(trade.createdAt),
formattedAmount: formattedAmount);
}
return Container();
});
});
return SafeArea(
child: Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).scaffoldBackgroundColor,
Theme.of(context).primaryColor
],
begin: Alignment.centerLeft,
end: Alignment.centerRight
)
),
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(top: 10, right: 10),
alignment: Alignment.centerRight,
child: SizedBox(
height: 44,
width: 44,
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: () async {
await showDialog<void>(
builder: (_) => MenuWidget(),
context: context
);
},
child: menuButton),
),
),
),
Padding(
padding: EdgeInsets.only(left: 20, top: 20),
child: WalletCard(),
),
SizedBox(
height: 28,
),
Expanded(child: TradeHistoryPanel())
],
),
),
),
);
}
}
@@ -1,81 +0,0 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/domain/common/transaction_direction.dart';
import 'package:cake_wallet/generated/i18n.dart';
class TransactionRow extends StatelessWidget {
TransactionRow(
{this.direction,
this.formattedDate,
this.formattedAmount,
this.formattedFiatAmount,
this.isPending,
@required this.onTap});
final VoidCallback onTap;
final TransactionDirection direction;
final String formattedDate;
final String formattedAmount;
final String formattedFiatAmount;
final bool isPending;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
padding: EdgeInsets.only(top: 14, bottom: 14, left: 20, right: 20),
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: PaletteDark.darkGrey,
width: 0.5,
style: BorderStyle.solid))),
child: Row(children: <Widget>[
Image.asset(
direction == TransactionDirection.incoming
? 'assets/images/transaction_incoming.png'
: 'assets/images/transaction_outgoing.png',
height: 25,
width: 25),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
(direction == TransactionDirection.incoming
? S.of(context).received
: S.of(context).sent) +
(isPending ? S.of(context).pending : ''),
style: TextStyle(
fontSize: 16,
color: Theme.of(context)
.primaryTextTheme
.subhead
.color)),
Text(formattedAmount,
style: const TextStyle(
fontSize: 16, color: Palette.purpleBlue))
]),
SizedBox(height: 6),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(formattedDate,
style: const TextStyle(
fontSize: 13, color: Palette.blueGrey)),
Text(formattedFiatAmount,
style: const TextStyle(
fontSize: 14, color: Palette.blueGrey))
]),
],
),
))
]),
));
}
}
+28 -25
View File
@@ -4,18 +4,29 @@ import 'package:provider/provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
class WalletMenu {
WalletMenu(this.context);
final List<String> items = [
S.current.reconnect,
S.current.rescan,
S.current.wallets,
S.current.nodes,
S.current.show_seed,
S.current.show_keys,
S.current.accounts,
S.current.address_book_menu
S.current.address_book_menu,
S.current.settings_title
];
final List<Image> images = [
Image.asset('assets/images/reconnect.png'),
Image.asset('assets/images/wallet.png'),
Image.asset('assets/images/nodes.png'),
Image.asset('assets/images/eye.png'),
Image.asset('assets/images/key.png'),
Image.asset('assets/images/open_book.png'),
Image.asset('assets/images/settings.png'),
];
final BuildContext context;
@@ -26,11 +37,10 @@ class WalletMenu {
_presentReconnectAlert(context);
break;
case 1:
Navigator.of(context).pushNamed(Routes.rescan);
Navigator.of(context).pushNamed(Routes.walletList);
break;
case 2:
Navigator.of(context).pushNamed(Routes.walletList);
Navigator.of(context).pushNamed(Routes.nodeList);
break;
case 3:
Navigator.of(context).pushNamed(Routes.auth,
@@ -49,10 +59,10 @@ class WalletMenu {
: null);
break;
case 5:
Navigator.of(context).pushNamed(Routes.accountList);
Navigator.of(context).pushNamed(Routes.addressBook);
break;
case 6:
Navigator.of(context).pushNamed(Routes.addressBook);
Navigator.of(context).pushNamed(Routes.settings);
break;
default:
break;
@@ -65,23 +75,16 @@ class WalletMenu {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
S.of(context).reconnection,
textAlign: TextAlign.center,
),
content: Text(S.of(context).reconnect_alert_text),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(S.of(context).cancel)),
FlatButton(
onPressed: () {
walletStore.reconnect();
Navigator.of(context).pop();
},
child: Text(S.of(context).ok))
],
return AlertWithTwoActions(
alertTitle: S.of(context).reconnection,
alertContent: S.of(context).reconnect_alert_text,
leftButtonText: S.of(context).ok,
rightButtonText: S.of(context).cancel,
actionLeftButton: () {
walletStore.reconnect();
Navigator.of(context).pop();
},
actionRightButton: () => Navigator.of(context).pop()
);
});
}
@@ -0,0 +1,298 @@
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/src/stores/action_list/action_list_store.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/routes.dart';
import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker;
import 'package:cake_wallet/themes.dart';
import 'package:cake_wallet/theme_changer.dart';
class ButtonHeader extends SliverPersistentHeaderDelegate {
final sendImage = Image.asset('assets/images/send.png');
final exchangeImage = Image.asset('assets/images/exchange.png');
final buyImage = Image.asset('assets/images/coins.png');
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
final actionListStore = Provider.of<ActionListStore>(context);
final historyPanelWidth = MediaQuery.of(context).size.width;
final _themeChanger = Provider.of<ThemeChanger>(context);
Image filterButton;
if (_themeChanger.getTheme() == Themes.darkTheme) {
filterButton = Image.asset('assets/images/filter_button.png');
} else {
filterButton = Image.asset('assets/images/filter_light_button.png');
}
double buttonsOpacity = 1 - shrinkOffset / (maxExtent - minExtent);
double buttonsHeight = maxExtent - minExtent - shrinkOffset;
buttonsOpacity = buttonsOpacity >= 0 ? buttonsOpacity : 0;
buttonsHeight = buttonsHeight >= 0 ? buttonsHeight : 0;
return Stack(
fit: StackFit.expand,
overflow: Overflow.visible,
children: <Widget>[
Opacity(
opacity: buttonsOpacity,
child: Container(
height: buttonsHeight,
padding: EdgeInsets.only(left: 44, right: 44),
child: Row(
children: <Widget>[
Flexible(
child: actionButton(
context: context,
image: sendImage,
title: S.of(context).send,
route: Routes.send
)
),
Flexible(
child: actionButton(
context: context,
image: exchangeImage,
title: S.of(context).exchange,
route: Routes.exchange
)
),
],
),
),
),
Positioned(
top: buttonsHeight,
left: 0,
child: ClipRRect(
borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)),
child: Container(
width: historyPanelWidth,
height: 66,
padding: EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 10),
color: Theme.of(context).backgroundColor,
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Text(
S.of(context).transactions,
style: TextStyle(
fontSize: 20,
color: Theme.of(context).primaryTextTheme.title.color
),
),
Positioned(
right: 0,
child: PopupMenuButton<int>(
itemBuilder: (context) => [
PopupMenuItem(
enabled: false,
value: -1,
child: Text(S.of(context).transactions,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.caption.color))),
PopupMenuItem(
value: 0,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(S.of(context).incoming),
Checkbox(
value: actionListStore
.transactionFilterStore
.displayIncoming,
onChanged: (value) =>
actionListStore
.transactionFilterStore
.toggleIncoming(),
)
]))),
PopupMenuItem(
value: 1,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text(S.of(context).outgoing),
Checkbox(
value: actionListStore
.transactionFilterStore
.displayOutgoing,
onChanged: (value) =>
actionListStore
.transactionFilterStore
.toggleOutgoing(),
)
]))),
PopupMenuItem(
value: 2,
child:
Text(S.of(context).transactions_by_date)),
PopupMenuDivider(),
PopupMenuItem(
enabled: false,
value: -1,
child: Text(S.of(context).trades,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.caption.color))),
PopupMenuItem(
value: 3,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text('XMR.TO'),
Checkbox(
value: actionListStore
.tradeFilterStore
.displayXMRTO,
onChanged: (value) =>
actionListStore
.tradeFilterStore
.toggleDisplayExchange(
ExchangeProviderDescription
.xmrto),
)
]))),
PopupMenuItem(
value: 4,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text('Change.NOW'),
Checkbox(
value: actionListStore
.tradeFilterStore
.displayChangeNow,
onChanged: (value) =>
actionListStore
.tradeFilterStore
.toggleDisplayExchange(
ExchangeProviderDescription
.changeNow),
)
]))),
PopupMenuItem(
value: 5,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text('MorphToken'),
Checkbox(
value: actionListStore
.tradeFilterStore
.displayMorphToken,
onChanged: (value) =>
actionListStore
.tradeFilterStore
.toggleDisplayExchange(
ExchangeProviderDescription
.morphToken),
)
])))
],
child: filterButton,
onSelected: (item) async {
if (item == 2) {
final List<DateTime> picked =
await date_rage_picker.showDatePicker(
context: context,
initialFirstDate: DateTime.now()
.subtract(Duration(days: 1)),
initialLastDate: (DateTime.now()),
firstDate: DateTime(2015),
lastDate: DateTime.now()
.add(Duration(days: 1)));
if (picked != null && picked.length == 2) {
actionListStore.transactionFilterStore
.changeStartDate(picked.first);
actionListStore.transactionFilterStore
.changeEndDate(picked.last);
}
}
},
),
)
],
),
),
)
)
],
);
}
@override
double get maxExtent => 174;
@override
double get minExtent => 66;
@override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
Widget actionButton({
BuildContext context,
@required Image image,
@required String title,
@required String route}) {
return Container(
width: MediaQuery.of(context).size.width,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
GestureDetector(
onTap: () {
if (route.isNotEmpty) {
Navigator.of(context, rootNavigator: true).pushNamed(route);
}
},
child: Container(
height: 48,
width: 48,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context).primaryTextTheme.subhead.color,
shape: BoxShape.circle
),
child: image,
),
),
Padding(
padding: EdgeInsets.only(top: 10),
child: Text(
title,
style: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.caption.color
),
),
)
],
),
);
}
}
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:intl/intl.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:provider/provider.dart';
@@ -20,8 +19,8 @@ class DateSectionRaw extends StatelessWidget {
final settingsStore = Provider.of<SettingsStore>(context);
final currentLanguage = settingsStore.languageCode;
final dateSectionDateFormat = settingsStore.getCurrentDateFormat(
formatUSA: "MMM d",
formatDefault: "d MMM");
formatUSA: "yyyy MMM d",
formatDefault: "d MMM yyyy");
var title = "";
if (isToday) {
@@ -35,11 +34,22 @@ class DateSectionRaw extends StatelessWidget {
title = dateSectionDateFormat.format(date);
}
return Padding(
padding: const EdgeInsets.only(top: 10, bottom: 10),
child: Center(
child: Text(title,
style: TextStyle(fontSize: 16, color: Palette.wildDarkBlue))),
return Container(
height: 36,
padding: EdgeInsets.only(top: 10, bottom: 10, left: 20, right: 20),
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor,
border: Border.all(
width: 1,
color: Theme.of(context).backgroundColor
),
),
child: Text(title,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).primaryTextTheme.headline.color
))
);
}
}
@@ -0,0 +1,264 @@
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:provider/provider.dart';
class MenuWidget extends StatefulWidget {
@override
MenuWidgetState createState() => MenuWidgetState();
}
class MenuWidgetState extends State<MenuWidget> {
final moneroIcon = Image.asset('assets/images/monero.png');
final largeScreen = 731;
double menuWidth;
double screenWidth;
double screenHeight;
double opacity;
bool isDraw;
double headerHeight;
double tileHeight;
double fromTopEdge;
double fromBottomEdge;
@override
void initState() {
menuWidth = 0;
screenWidth = 0;
screenHeight = 0;
opacity = 0;
isDraw = false;
headerHeight = 120;
tileHeight = 75;
fromTopEdge = 50;
fromBottomEdge = 30;
super.initState();
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
}
void afterLayout(dynamic _) {
screenWidth = MediaQuery.of(context).size.width;
screenHeight = MediaQuery.of(context).size.height;
setState(() {
menuWidth = screenWidth;
opacity = 1;
if (screenHeight > largeScreen) {
final scale = screenHeight / largeScreen;
tileHeight *= scale;
headerHeight *= scale;
fromTopEdge *= scale;
fromBottomEdge *= scale;
}
});
Timer(Duration(milliseconds: 350), () =>
setState(() => isDraw = true)
);
}
@override
Widget build(BuildContext context) {
final walletMenu = WalletMenu(context);
final walletStore = Provider.of<WalletStore>(context);
final itemCount = walletMenu.items.length;
return GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
color: Colors.transparent,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 24),
child: isDraw
? Container(
height: 60,
width: 4,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(2)),
color: Theme.of(context).hintColor //
),
)
: Container(
height: 60,
width: 4,
)
),
SizedBox(width: 12),
Expanded(
child: GestureDetector(
onTap: () => null,
child: Container(
width: double.infinity,
height: double.infinity,
alignment: Alignment.centerRight,
child: AnimatedContainer(
alignment: Alignment.centerLeft,
width: menuWidth,
height: double.infinity,
duration: Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(24), bottomLeft: Radius.circular(24)),
color: Theme.of(context).primaryTextTheme.display1.color.withOpacity(opacity)
),
child: isDraw
? ListView.separated(
itemBuilder: (_, index) {
if (index == 0) {
return Container(
height: headerHeight,
padding: EdgeInsets.only(
left: 24,
top: fromTopEdge,
right: 24,
bottom: fromBottomEdge),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(24)),
color: Theme.of(context).primaryTextTheme.display2.color
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
moneroIcon,
SizedBox(width: 16),
Expanded(
child: Container(
height: 40,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
walletStore.name,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.title.color,
decoration: TextDecoration.none,
fontFamily: 'Lato',
fontSize: 20,
fontWeight: FontWeight.bold
),
),
Text(
walletStore.account.label,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
decoration: TextDecoration.none,
fontFamily: 'Lato',
fontSize: 12
),
)
],
),
)
)
],
),
);
}
index -= 1;
final item = walletMenu.items[index];
final image = walletMenu.images[index] ?? Offstage();
return GestureDetector(
onTap: () {
Navigator.of(context).pop();
walletMenu.action(index);
},
child: index == itemCount - 1
? Container(
height: headerHeight,
padding: EdgeInsets.only(
left: 24,
right: 24,
top: fromBottomEdge,
bottom: fromTopEdge),
alignment: Alignment.topLeft,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24)),
color: Theme.of(context).primaryTextTheme.display1.color,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
image,
SizedBox(width: 16),
Expanded(
child: Text(
item,
style: TextStyle(
decoration: TextDecoration.none,
color: Theme.of(context).primaryTextTheme.title.color,
fontFamily: 'Lato',
fontSize: 20,
fontWeight: FontWeight.bold
),
)
)
],
),
)
: Container(
height: tileHeight,
padding: EdgeInsets.only(left: 24, right: 24),
color: Theme.of(context).primaryTextTheme.display1.color,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
image,
SizedBox(width: 16),
Expanded(
child: Text(
item,
style: TextStyle(
decoration: TextDecoration.none,
color: Theme.of(context).primaryTextTheme.title.color,
fontFamily: 'Lato',
fontSize: 20,
fontWeight: FontWeight.bold
),
)
)
],
),
),
);
},
separatorBuilder: (_, index) =>
Container(
height: 1,
color: Theme.of(context).dividerColor,
),
itemCount: itemCount + 1)
: Offstage()
),
),
)
)
],
)
),
),
),
);
}
}
@@ -0,0 +1,162 @@
import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
import 'package:cake_wallet/src/stores/action_list/action_list_store.dart';
import 'package:cake_wallet/src/stores/action_list/date_section_item.dart';
import 'package:cake_wallet/src/stores/action_list/trade_list_item.dart';
import 'package:cake_wallet/src/stores/action_list/transaction_list_item.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/routes.dart';
import 'date_section_raw.dart';
import 'trade_row.dart';
import 'transaction_raw.dart';
import 'button_header.dart';
class TradeHistoryPanel extends StatefulWidget {
@override
TradeHistoryPanelState createState() => TradeHistoryPanelState();
}
class TradeHistoryPanelState extends State<TradeHistoryPanel> {
final _listObserverKey = GlobalKey();
final _listKey = GlobalKey();
double panelHeight;
double screenHeight;
@override
void initState() {
panelHeight = 0;
screenHeight = 0;
super.initState();
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
}
void afterLayout(dynamic _) {
screenHeight = MediaQuery.of(context).size.height;
setState(() {
panelHeight = screenHeight;
});
}
@override
Widget build(BuildContext context) {
final actionListStore = Provider.of<ActionListStore>(context);
final settingsStore = Provider.of<SettingsStore>(context);
final transactionDateFormat = DateFormat("HH:mm");
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
alignment: Alignment.bottomCenter,
child: AnimatedContainer(
width: MediaQuery.of(context).size.width,
height: panelHeight,
duration: Duration(milliseconds: 1000),
curve: Curves.fastOutSlowIn,
child: ClipRRect(
borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)),
child: CustomScrollView(
slivers: <Widget>[
SliverPersistentHeader(
delegate: ButtonHeader(),
pinned: true,
floating: false,
),
Observer(
key: _listObserverKey,
builder: (_) {
final items = actionListStore.items == null
? <String>[]
: actionListStore.items;
final itemsCount = items.length + 1;
final symbol = settingsStore.fiatCurrency.toString();
double freeSpaceHeight = MediaQuery.of(context).size.height - 496;
return SliverList(
key: _listKey,
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index == itemsCount - 1) {
freeSpaceHeight = freeSpaceHeight >= 0 ? freeSpaceHeight : 0;
return Container(
height: freeSpaceHeight,
width: MediaQuery.of(context).size.width,
color: Theme.of(context).backgroundColor,
);
}
final item = items[index];
if (item is DateSectionItem) {
freeSpaceHeight -= 38;
return DateSectionRaw(date: item.date);
}
if (item is TransactionListItem) {
freeSpaceHeight -= 62;
final transaction = item.transaction;
final savedDisplayMode = settingsStore.balanceDisplayMode;
final formattedAmount =
savedDisplayMode == BalanceDisplayMode.hiddenBalance
? '---'
: transaction.amountFormatted();
final formattedFiatAmount =
savedDisplayMode == BalanceDisplayMode.hiddenBalance
? '---'
: transaction.fiatAmount(); // symbol ???
return TransactionRow(
onTap: () => Navigator.of(context).pushNamed(
Routes.transactionDetails,
arguments: transaction),
direction: transaction.direction,
formattedDate:
transactionDateFormat.format(transaction.date),
formattedAmount: formattedAmount,
formattedFiatAmount: formattedFiatAmount,
isPending: transaction.isPending);
}
if (item is TradeListItem) {
freeSpaceHeight -= 62;
final trade = item.trade;
final savedDisplayMode = settingsStore.balanceDisplayMode;
final formattedAmount = trade.amount != null
? savedDisplayMode == BalanceDisplayMode.hiddenBalance
? '---'
: trade.amountFormatted()
: trade.amount;
return TradeRow(
onTap: () => Navigator.of(context)
.pushNamed(Routes.tradeDetails, arguments: trade),
provider: trade.provider,
from: trade.from,
to: trade.to,
createdAtFormattedDate:
transactionDateFormat.format(trade.createdAt),
formattedAmount: formattedAmount);
}
return Container(
color: Theme.of(context).backgroundColor
);
},
childCount: itemsCount
)
);
})
],
),
)
),
);
}
}
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
@@ -21,25 +20,33 @@ class TradeRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
final amountCrypto = provider == ExchangeProviderDescription.xmrto
? to.toString()
: from.toString();
final amountCrypto = from.toString();
return InkWell(
onTap: onTap,
child: Container(
padding: EdgeInsets.only(top: 14, bottom: 14, left: 20, right: 20),
height: 60,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
color: PaletteDark.darkGrey,
width: 0.5,
style: BorderStyle.solid))),
color: Theme.of(context).backgroundColor,
border: Border.all(
width: 1,
color: Theme.of(context).backgroundColor
),
),
padding: EdgeInsets.only(top: 5, bottom: 5, left: 20, right: 20),
child: Row(children: <Widget>[
_getPoweredImage(provider),
Container(
height: 36,
width: 36,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).backgroundColor
),
child: _getPoweredImage(provider),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
padding: const EdgeInsets.only(left: 10),
child: Column(
children: <Widget>[
Row(
@@ -48,23 +55,23 @@ class TradeRow extends StatelessWidget {
Text('${from.toString()} → ${to.toString()}',
style: TextStyle(
fontSize: 16,
color: Theme.of(context)
.primaryTextTheme
.subhead
.color)),
color: Theme.of(context).primaryTextTheme.title.color
)),
formattedAmount != null
? Text(formattedAmount + ' ' + amountCrypto,
style: const TextStyle(
fontSize: 16, color: Palette.purpleBlue))
style: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.title.color
))
: Container()
]),
SizedBox(height: 6),
SizedBox(height: 5),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(createdAtFormattedDate,
style: const TextStyle(
fontSize: 13, color: Palette.blueGrey))
style: TextStyle(
fontSize: 14, color: Theme.of(context).primaryTextTheme.headline.color))
]),
],
),
@@ -0,0 +1,94 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/domain/common/transaction_direction.dart';
import 'package:cake_wallet/generated/i18n.dart';
class TransactionRow extends StatelessWidget {
TransactionRow(
{this.direction,
this.formattedDate,
this.formattedAmount,
this.formattedFiatAmount,
this.isPending,
@required this.onTap});
final VoidCallback onTap;
final TransactionDirection direction;
final String formattedDate;
final String formattedAmount;
final String formattedFiatAmount;
final bool isPending;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
height: 60,
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor,
border: Border.all(
width: 1,
color: Theme.of(context).backgroundColor
),
),
padding: EdgeInsets.only(top: 5, bottom: 5, left: 20, right: 20),
child: Row(children: <Widget>[
Container(
height: 36,
width: 36,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).primaryTextTheme.display3.color
),
child: Image.asset(
direction == TransactionDirection.incoming
? 'assets/images/down_arrow.png'
: 'assets/images/up_arrow.png'),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
(direction == TransactionDirection.incoming
? S.of(context).received
: S.of(context).sent) +
(isPending ? S.of(context).pending : ''),
style: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.title.color
)),
Text(direction == TransactionDirection.incoming
? formattedAmount
: '- ' + formattedAmount,
style: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.title.color
))
]),
SizedBox(height: 5,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(formattedDate,
style: TextStyle(
fontSize: 14, color: Theme.of(context).primaryTextTheme.headline.color)),
Text(direction == TransactionDirection.incoming
? formattedFiatAmount
: '- ' + formattedFiatAmount,
style: TextStyle(
fontSize: 14, color: Theme.of(context).primaryTextTheme.headline.color))
]),
],
),
))
]),
));
}
}
@@ -0,0 +1,482 @@
import 'dart:async';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/src/stores/balance/balance_store.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/src/stores/sync/sync_store.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/sync_status.dart';
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
import 'package:cake_wallet/routes.dart';
class WalletCard extends StatefulWidget {
@override
WalletCardState createState() => WalletCardState();
}
class WalletCardState extends State<WalletCard> {
final _syncingObserverKey = GlobalKey();
final _balanceObserverKey = GlobalKey();
final _addressObserverKey = GlobalKey();
double cardWidth;
double cardHeight;
double screenWidth;
double opacity;
bool isDraw;
bool isFrontSide;
@override
void initState() {
cardWidth = 0;
cardHeight = 220;
screenWidth = 0;
opacity = 0;
isDraw = false;
isFrontSide = true;
super.initState();
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
}
void afterLayout(dynamic _) {
screenWidth = MediaQuery.of(context).size.width - 20;
setState(() {
cardWidth = screenWidth;
opacity = 1;
});
Timer(Duration(milliseconds: 500), () =>
setState(() => isDraw = true)
);
}
@override
Widget build(BuildContext context) {
final List<Color> colorsSync = [
Theme.of(context).cardTheme.color,
Theme.of(context).hoverColor
];
return Container(
width: double.infinity,
height: cardHeight,
alignment: Alignment.centerRight,
child: AnimatedContainer(
alignment: Alignment.centerLeft,
width: cardWidth,
height: cardHeight,
duration: Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn,
padding: EdgeInsets.only(
top: 1,
left: 1,
bottom: 1
),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
color: Theme.of(context).focusColor,
boxShadow: [
BoxShadow(
color: PaletteDark.darkNightBlue.withOpacity(0.5),
blurRadius: 8,
offset: Offset(5, 5))
]
),
child: ClipRRect(
borderRadius: BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
child: Container(
width: cardWidth,
height: cardHeight,
color: Theme.of(context).cardColor,
child: InkWell(
onTap: () => setState(() => isFrontSide = !isFrontSide),
child: isFrontSide
? frontSide(colorsSync)
: backSide(colorsSync)
),
),
)
),
);
}
Widget frontSide(List<Color> colorsSync) {
final syncStore = Provider.of<SyncStore>(context);
final walletStore = Provider.of<WalletStore>(context);
final settingsStore = Provider.of<SettingsStore>(context);
final balanceStore = Provider.of<BalanceStore>(context);
final triangleButton = Image.asset('assets/images/triangle.png',
color: Theme.of(context).primaryTextTheme.title.color,
);
return Observer(
key: _syncingObserverKey,
builder: (_) {
final status = syncStore.status;
final statusText = status.title();
final progress = syncStore.status.progress();
final indicatorWidth = progress * cardWidth;
String shortAddress = walletStore.subaddress.address;
shortAddress = shortAddress.replaceRange(4, shortAddress.length - 4, '...');
var descriptionText = '';
if (status is SyncingSyncStatus) {
descriptionText = S
.of(context)
.Blocks_remaining(
syncStore.status.toString());
}
if (status is FailedSyncStatus) {
descriptionText = S
.of(context)
.please_try_to_connect_to_another_node;
}
return Container(
width: cardWidth,
height: cardHeight,
child: Stack(
children: <Widget>[
Container(
height: cardHeight,
width: indicatorWidth,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
gradient: LinearGradient(
colors: colorsSync,
begin: Alignment.topCenter,
end: Alignment.bottomCenter
)
),
),
progress != 1
? Positioned(
left: indicatorWidth,
top: 0,
bottom: 0,
child: Container(
width: 1,
height: cardHeight,
color: Theme.of(context).focusColor,
)
)
: Offstage(),
isDraw ? Positioned(
left: 20,
right: 20,
top: 30,
bottom: 30,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InkWell(
onTap: () {},
child: Row(
children: <Widget>[
Text(
walletStore.name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color
),
),
SizedBox(width: 10),
triangleButton
],
),
),
SizedBox(
height: 5,
),
Text(
walletStore.account.label,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).primaryTextTheme.caption.color
),
)
],
),
Container(
width: 98,
height: 32,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context).accentTextTheme.subtitle.backgroundColor,
borderRadius: BorderRadius.all(Radius.circular(16))
),
child: Text(
shortAddress,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).primaryTextTheme.caption.color
),
),
)
],
),
status is SyncedSyncStatus
? Observer(
key: _balanceObserverKey,
builder: (_) {
final balanceDisplayMode = settingsStore.balanceDisplayMode;
final symbol = settingsStore
.fiatCurrency
.toString();
var balance = '---';
var fiatBalance = '---';
if (balanceDisplayMode ==
BalanceDisplayMode.availableBalance) {
balance =
balanceStore.unlockedBalance ??
'0.0';
fiatBalance =
'$symbol ${balanceStore.fiatUnlockedBalance}';
}
if (balanceDisplayMode ==
BalanceDisplayMode.fullBalance) {
balance =
balanceStore.fullBalance ?? '0.0';
fiatBalance =
'$symbol ${balanceStore.fiatFullBalance}';
}
return Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
balanceDisplayMode.toString(),
style: TextStyle(
fontSize: 12,
color: Theme.of(context).primaryTextTheme.caption.color
),
),
SizedBox(height: 5),
Text(
balance,
style: TextStyle(
fontSize: 28,
color: Theme.of(context).primaryTextTheme.title.color
),
)
],
),
Text(
fiatBalance,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryTextTheme.title.color
),
)
],
);
}
)
: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
statusText,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).primaryTextTheme.caption.color
),
),
SizedBox(height: 5),
Text(
descriptionText,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryTextTheme.title.color
),
)
],
)
],
)
],
),
)
)
: Offstage()
],
),
);
},
);
}
Widget backSide(List<Color> colorsSync) {
final walletStore = Provider.of<WalletStore>(context);
final rightArrow = Image.asset('assets/images/right_arrow.png',
color: Theme.of(context).primaryTextTheme.title.color,
);
double messageBoxHeight = 0;
double messageBoxWidth = cardWidth - 10;
return Observer(
key: _addressObserverKey,
builder: (_) {
return Container(
width: cardWidth,
height: cardHeight,
alignment: Alignment.topCenter,
child: Stack(
alignment: Alignment.topRight,
children: <Widget>[
Container(
width: cardWidth,
height: cardHeight,
padding: EdgeInsets.only(left: 20, right: 20, top: 30, bottom: 30),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
gradient: LinearGradient(
colors: colorsSync,
begin: Alignment.topCenter,
end: Alignment.bottomCenter
)
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Container(
height: 90,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
S.current.card_address,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).primaryTextTheme.caption.color
),
),
GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(
text: walletStore.subaddress.address));
_addressObserverKey.currentState.setState(() {
messageBoxHeight = 20;
messageBoxWidth = cardWidth;
});
Timer(Duration(milliseconds: 1000), () {
try {
_addressObserverKey.currentState.setState(() {
messageBoxHeight = 0;
messageBoxWidth = cardWidth - 10;
});
} catch(e) {
print('${e.toString()}');
}
});
},
child: Text(
walletStore.subaddress.address,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryTextTheme.title.color
),
),
)
],
),
)
),
SizedBox(width: 10),
Container(
width: 90,
height: 90,
child: QrImage(
data: walletStore.subaddress.address,
backgroundColor: Colors.transparent,
foregroundColor: Theme.of(context).primaryTextTheme.caption.color
),
)
],
),
Container(
height: 44,
padding: EdgeInsets.only(left: 20, right: 20),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(22)),
color: Theme.of(context).primaryTextTheme.overline.color
),
child: InkWell(
onTap: () => Navigator.of(context,
rootNavigator: true)
.pushNamed(Routes.receive),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
S.of(context).accounts_subaddresses,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryTextTheme.title.color
),
),
rightArrow
],
),
),
)
],
),
),
AnimatedContainer(
width: messageBoxWidth,
height: messageBoxHeight,
alignment: Alignment.center,
duration: Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(10)),
color: Colors.green
),
child: Text(
S.of(context).copied_to_clipboard,
style: TextStyle(
fontSize: 10,
color: Colors.white
),
),
)
],
),
);
}
);
}
}
+235 -238
View File
@@ -2,19 +2,17 @@ import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:cake_wallet/palette.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/generated/i18n.dart';
class DisclaimerPage extends BasePage {
DisclaimerPage({this.isReadOnly = false});
final bool isReadOnly;
@override
bool get isModalBackButton => false;
@override
String get title => 'Terms of Use';
@@ -34,7 +32,7 @@ class DisclaimerPageBody extends StatefulWidget {
class DisclaimerBodyState extends State<DisclaimerPageBody> {
DisclaimerBodyState(this._isAccepted);
static const xmrtoUrl = 'https://xmr.to/app_static/html/tos.html';
static const xmrtoUrl = 'https://xmr.to/terms-of-service';
static const changenowUrl = 'https://changenow.io/terms-of-use';
static const morphUrl = 'http://morphtoken.com/terms';
@@ -55,22 +53,11 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
'Terms and conditions',
textAlign: TextAlign.center,
),
content: Text(
'By using this app, you agree to the Terms of Agreement set forth to below',
textAlign: TextAlign.center,
),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('OK')),
],
return AlertWithOneAction(
alertTitle: 'Terms and conditions',
alertContent: 'By using this app, you agree to the Terms of Agreement set forth to below',
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
}
@@ -86,245 +73,255 @@ class DisclaimerBodyState extends State<DisclaimerPageBody> {
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
SizedBox(height: 10.0),
Expanded(
child: Stack(
children: <Widget>[
SingleChildScrollView(
padding: EdgeInsets.only(left: 25.0, right: 25.0),
child: Column(
return Container(
color: Theme.of(context).backgroundColor,
child: Column(
children: <Widget>[
SizedBox(height: 10.0),
Expanded(
child: Stack(
children: <Widget>[
!_isAccepted
? Row(
SingleChildScrollView(
padding: EdgeInsets.only(left: 24.0, right: 24.0),
child: Column(
children: <Widget>[
!_isAccepted
? Row(
children: <Widget>[
Expanded(
child: Text(
'Terms and conditions',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold),
fontSize: 20.0,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color
),
),
)
],
)
: Offstage(),
!_isAccepted
? SizedBox(
: Offstage(),
!_isAccepted
? SizedBox(
height: 20.0,
)
: Offstage(),
Row(
children: <Widget>[
Expanded(
child: Text(
'Legal Disclaimer\nAnd\nTerms of Use',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12.0, fontWeight: FontWeight.bold),
),
)
],
),
SizedBox(
height: 16.0,
),
Row(
children: <Widget>[
Expanded(
child: Text(
_fileText,
style: TextStyle(fontSize: 12.0),
))
],
),
SizedBox(
height: 16.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: Text(
'Other Terms and Conditions',
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 14.0, fontWeight: FontWeight.bold),
),
)
],
),
SizedBox(
height: 16.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: GestureDetector(
onTap: () => launchUrl(xmrtoUrl),
child: Text(
xmrtoUrl,
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.blue,
fontSize: 14.0,
fontWeight: FontWeight.normal,
decoration: TextDecoration.underline),
),
))
],
),
SizedBox(
height: 16.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: GestureDetector(
onTap: () => launchUrl(changenowUrl),
child: Text(
changenowUrl,
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.blue,
fontSize: 14.0,
fontWeight: FontWeight.normal,
decoration: TextDecoration.underline),
),
))
],
),
SizedBox(
height: 16.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: GestureDetector(
onTap: () => launchUrl(morphUrl),
child: Text(
morphUrl,
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.blue,
fontSize: 14.0,
fontWeight: FontWeight.normal,
decoration: TextDecoration.underline),
),
))
],
),
SizedBox(
height: 16.0,
)
],
),
),
Container(
alignment: Alignment.bottomCenter,
child: Container(
height: 12.0,
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 0.7, sigmaY: 0.7),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).backgroundColor.withOpacity(0.0),
Theme.of(context).backgroundColor,
: Offstage(),
Row(
children: <Widget>[
Expanded(
child: Text(
'Legal Disclaimer\nAnd\nTerms of Use',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color
),
),
)
],
begin: FractionalOffset.topCenter,
end: FractionalOffset.bottomCenter,
),
SizedBox(
height: 16.0,
),
Row(
children: <Widget>[
Expanded(
child: Text(
_fileText,
style: TextStyle(
fontSize: 12.0,
color: Theme.of(context).primaryTextTheme.title.color
),
))
],
),
SizedBox(
height: 16.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: Text(
'Other Terms and Conditions',
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color
),
),
)
],
),
SizedBox(
height: 16.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: GestureDetector(
onTap: () => launchUrl(xmrtoUrl),
child: Text(
xmrtoUrl,
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.blue,
fontSize: 14.0,
fontWeight: FontWeight.normal,
decoration: TextDecoration.underline),
),
))
],
),
SizedBox(
height: 16.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: GestureDetector(
onTap: () => launchUrl(changenowUrl),
child: Text(
changenowUrl,
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.blue,
fontSize: 14.0,
fontWeight: FontWeight.normal,
decoration: TextDecoration.underline),
),
))
],
),
SizedBox(
height: 16.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: GestureDetector(
onTap: () => launchUrl(morphUrl),
child: Text(
morphUrl,
textAlign: TextAlign.left,
style: TextStyle(
color: Colors.blue,
fontSize: 14.0,
fontWeight: FontWeight.normal,
decoration: TextDecoration.underline),
),
))
],
),
SizedBox(
height: 16.0,
)
],
),
),
Container(
alignment: Alignment.bottomCenter,
child: Container(
height: 12.0,
child: ClipRect(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 0.7, sigmaY: 0.7),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).backgroundColor.withOpacity(0.0),
Theme.of(context).backgroundColor,
],
begin: FractionalOffset.topCenter,
end: FractionalOffset.bottomCenter,
),
),
),
),
),
),
),
)
],
)),
if (!widget.isReadOnly) ...[
!_isAccepted
? Row(
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.only(
left: 24.0, top: 10.0, right: 24.0, bottom: 10.0),
child: InkWell(
onTap: () {
setState(() {
_checked = !_checked;
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
height: 24.0,
width: 24.0,
margin: EdgeInsets.only(
right: 10.0,
),
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context).primaryTextTheme.caption.color, width: 1.0),
borderRadius: BorderRadius.all(
Radius.circular(8.0)),
color: Theme.of(context).backgroundColor),
child: _checked
? Icon(
Icons.check,
color: Colors.blue,
size: 20.0,
)
: null,
),
Text(
'I agree to Terms of Use',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.title.color
),
)
],
),
)),
),
],
)
: Offstage(),
!_isAccepted
? Container(
padding:
EdgeInsets.only(left: 24.0, right: 24.0, bottom: 24.0),
child: PrimaryButton(
onPressed: _checked ? () {} : null,
text: 'Accept',
color: Colors.green,
textColor: Colors.white,
),
)
: Offstage(),
_isAccepted
? SizedBox(
height: 24.0,
)
: Offstage()
],
)),
if (!widget.isReadOnly) ...[
!_isAccepted
? Row(
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.only(
left: 25.0, top: 10.0, right: 25.0, bottom: 10.0),
child: InkWell(
onTap: () {
setState(() {
_checked = !_checked;
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
height: 25.0,
width: 25.0,
margin: EdgeInsets.only(
right: 10.0,
),
decoration: BoxDecoration(
border: Border.all(
color: Palette.lightGrey, width: 1.0),
borderRadius: BorderRadius.all(
Radius.circular(8.0)),
color: Theme.of(context).backgroundColor),
child: _checked
? Icon(
Icons.check,
color: Colors.blue,
size: 20.0,
)
: null,
),
Text(
'I agree to Terms of Use',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14.0),
)
],
),
)),
),
],
)
: Offstage(),
!_isAccepted
? Container(
padding:
EdgeInsets.only(left: 25.0, right: 25.0, bottom: 25.0),
child: PrimaryButton(
onPressed: _checked ? () {} : null,
text: 'Accept',
color: Theme.of(context)
.primaryTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.primaryTextTheme
.button
.decorationColor,
),
)
: Offstage(),
_isAccepted
? SizedBox(
height: 20.0,
)
: Offstage()
],
],
),
);
}
}
+19 -414
View File
@@ -1,69 +1,31 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart';
import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/stores/exchange/exchange_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/base_exchange_widget.dart';
class ExchangePage extends BasePage {
@override
String get title => S.current.exchange;
@override
bool get isModalBackButton => true;
Color get backgroundLightColor => Palette.darkLavender;
final Image arrowBottomPurple =
Image.asset('assets/images/arrow_bottom_purple_icon.png', height: 8);
@override
Color get backgroundDarkColor => PaletteDark.moderateBlue;
@override
Widget middle(BuildContext context) {
final exchangeStore = Provider.of<ExchangeStore>(context);
return FlatButton(
onPressed: () => _presentProviderPicker(context),
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(S.of(context).exchange,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w400,
color: Theme.of(context).primaryTextTheme.title.color)),
SizedBox(width: 5),
arrowBottomPurple
]),
Observer(
builder: (_) => Text('${exchangeStore.provider.title}',
style: TextStyle(
fontSize: 10.0,
fontWeight: FontWeight.w400,
color:
Theme.of(context).primaryTextTheme.headline.color)))
],
),
);
return PresentProviderPicker(exchangeStore: exchangeStore);
}
@override
@@ -72,14 +34,16 @@ class ExchangePage extends BasePage {
return ButtonTheme(
minWidth: double.minPositive,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: FlatButton(
padding: EdgeInsets.all(0),
child: Text(
S.of(context).clear,
style: TextStyle(
color: Theme.of(context).accentTextTheme.caption.color,
color: Theme.of(context).primaryTextTheme.caption.color,
fontWeight: FontWeight.w500,
fontSize: 16),
fontSize: 14),
),
onPressed: () => exchangeStore.reset()),
);
@@ -87,21 +51,6 @@ class ExchangePage extends BasePage {
@override
Widget body(BuildContext context) => ExchangeForm();
void _presentProviderPicker(BuildContext context) {
final exchangeStore = Provider.of<ExchangeStore>(context);
final items = exchangeStore.providersForCurrentPair();
final selectedItem = items.indexOf(exchangeStore.provider);
showDialog<void>(
builder: (_) => Picker(
items: items,
selectedAtIndex: selectedItem,
title: S.of(context).change_exchange_provider,
onItemSelected: (ExchangeProvider provider) =>
exchangeStore.changeProvider(provider: provider)),
context: context);
}
}
class ExchangeForm extends StatefulWidget {
@@ -110,362 +59,18 @@ class ExchangeForm extends StatefulWidget {
}
class ExchangeFormState extends State<ExchangeForm> {
final depositKey = GlobalKey<ExchangeCardState>();
final receiveKey = GlobalKey<ExchangeCardState>();
final _formKey = GlobalKey<FormState>();
var _isReactionsSet = false;
final Image arrowBottomPurple = Image.asset(
'assets/images/arrow_bottom_purple_icon.png',
height: 8,
);
final Image arrowBottomCakeGreen = Image.asset(
'assets/images/arrow_bottom_cake_green.png',
height: 8,
);
@override
Widget build(BuildContext context) {
final exchangeStore = Provider.of<ExchangeStore>(context);
final walletStore = Provider.of<WalletStore>(context);
final exchangeTemplateStore = Provider.of<ExchangeTemplateStore>(context);
final depositWalletName =
exchangeStore.depositCurrency == CryptoCurrency.xmr
? walletStore.name
: null;
final receiveWalletName =
exchangeStore.receiveCurrency == CryptoCurrency.xmr
? walletStore.name
: null;
WidgetsBinding.instance.addPostFrameCallback(
(_) => _setReactions(context, exchangeStore, walletStore));
return Form(
key: _formKey,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 20, right: 20),
content: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10, bottom: 20),
child: Text(
S.of(context).you_will_send,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
height: 1.1,
color: Theme.of(context).primaryTextTheme.title.color),
),
),
ExchangeCard(
key: depositKey,
initialCurrency: exchangeStore.depositCurrency,
initialWalletName: depositWalletName,
initialAddress:
exchangeStore.depositCurrency == walletStore.type
? walletStore.address
: null,
initialIsAmountEditable: true,
initialIsAddressEditable: true,
isAmountEstimated: false,
currencies: CryptoCurrency.all,
onCurrencySelected: (currency) =>
exchangeStore.changeDepositCurrency(currency: currency),
imageArrow: arrowBottomPurple,
currencyValueValidator: (value) {
exchangeStore.validateCryptoCurrency(value);
return exchangeStore.errorMessage;
},
addressTextFieldValidator: (value) {
exchangeStore.validateAddress(value,
cryptoCurrency: exchangeStore.depositCurrency);
return exchangeStore.errorMessage;
},
),
SizedBox(height: 35),
Padding(
padding: EdgeInsets.only(bottom: 20),
child: Text(
S.of(context).you_will_get,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18,
height: 1.1,
color:
Theme.of(context).primaryTextTheme.title.color),
)),
Observer(
builder: (_) => ExchangeCard(
key: receiveKey,
initialCurrency: exchangeStore.receiveCurrency,
initialWalletName: receiveWalletName,
initialAddress:
exchangeStore.receiveCurrency == walletStore.type
? walletStore.address
: null,
initialIsAmountEditable: false,
initialIsAddressEditable: true,
isAmountEstimated: true,
currencies: CryptoCurrency.all,
onCurrencySelected: (currency) => exchangeStore
.changeReceiveCurrency(currency: currency),
imageArrow: arrowBottomCakeGreen,
currencyValueValidator: (value) {
exchangeStore.validateCryptoCurrency(value);
return exchangeStore.errorMessage;
},
addressTextFieldValidator: (value) {
exchangeStore.validateAddress(value,
cryptoCurrency: exchangeStore.receiveCurrency);
return exchangeStore.errorMessage;
},
)),
],
),
),
bottomSectionPadding: EdgeInsets.only(top: 35, left: 20, right: 20),
bottomSection: Column(children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 15),
child: Observer(builder: (_) {
final description =
exchangeStore.provider is XMRTOExchangeProvider
? S.of(context).amount_is_guaranteed
: S.of(context).amount_is_estimate;
return Center(
child: Text(
description,
style: TextStyle(color: Palette.blueGrey, fontSize: 12),
),
);
}),
),
Observer(
builder: (_) => LoadingPrimaryButton(
text: S.of(context).exchange,
onPressed: () {
if (_formKey.currentState.validate()) {
exchangeStore.createTrade();
}
},
color: Theme.of(context)
.primaryTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.primaryTextTheme
.button
.decorationColor,
isLoading: exchangeStore.tradeState is TradeIsCreating,
)),
Observer(builder: (_) {
final title = exchangeStore.provider.description.title;
var imageSrc = '';
switch (exchangeStore.provider.description) {
case ExchangeProviderDescription.xmrto:
imageSrc = 'assets/images/xmr_btc.png';
break;
case ExchangeProviderDescription.changeNow:
imageSrc = 'assets/images/change_now.png';
break;
case ExchangeProviderDescription.morphToken:
imageSrc = 'assets/images/morph_icon.png';
break;
}
return Padding(
padding: EdgeInsets.only(top: 20, bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(imageSrc),
SizedBox(width: 10),
Text(
S.of(context).powered_by(title),
style: TextStyle(fontSize: 14, color: Palette.powered),
)
],
),
);
})
]),
));
}
void _setReactions(
BuildContext context, ExchangeStore store, WalletStore walletStore) {
if (_isReactionsSet) {
return;
}
final depositAddressController = depositKey.currentState.addressController;
final depositAmountController = depositKey.currentState.amountController;
final receiveAddressController = receiveKey.currentState.addressController;
final receiveAmountController = receiveKey.currentState.amountController;
final limitsState = store.limitsState;
if (limitsState is LimitsLoadedSuccessfully) {
final min = limitsState.limits.min != null
? limitsState.limits.min.toString()
: null;
final max = limitsState.limits.max != null
? limitsState.limits.max.toString()
: null;
final key = depositKey;
key.currentState.changeLimits(min: min, max: max);
}
_onCurrencyChange(store.receiveCurrency, walletStore, receiveKey);
_onCurrencyChange(store.depositCurrency, walletStore, depositKey);
reaction(
(_) => walletStore.name,
(String _) => _onWalletNameChange(
walletStore, store.receiveCurrency, receiveKey));
reaction(
(_) => walletStore.name,
(String _) => _onWalletNameChange(
walletStore, store.depositCurrency, depositKey));
reaction(
(_) => store.receiveCurrency,
(CryptoCurrency currency) =>
_onCurrencyChange(currency, walletStore, receiveKey));
reaction(
(_) => store.depositCurrency,
(CryptoCurrency currency) =>
_onCurrencyChange(currency, walletStore, depositKey));
reaction((_) => store.depositAmount, (String amount) {
if (depositKey.currentState.amountController.text != amount) {
depositKey.currentState.amountController.text = amount;
}
});
reaction((_) => store.receiveAmount, (String amount) {
if (receiveKey.currentState.amountController.text !=
store.receiveAmount) {
receiveKey.currentState.amountController.text = amount;
}
});
reaction((_) => store.provider, (ExchangeProvider provider) {
receiveKey.currentState.isAddressEditable(isEditable: true);
receiveKey.currentState.isAmountEditable(isEditable: false);
depositKey.currentState.isAddressEditable(isEditable: true);
depositKey.currentState.isAmountEditable(isEditable: true);
receiveKey.currentState.changeIsAmountEstimated(true);
});
reaction((_) => store.tradeState, (ExchangeTradeState state) {
if (state is TradeIsCreatedFailure) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).error),
content: Text(state.error),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () => Navigator.of(context).pop())
],
);
});
});
}
if (state is TradeIsCreatedSuccessfully) {
Navigator.of(context)
.pushNamed(Routes.exchangeConfirm, arguments: state.trade);
}
});
reaction((_) => store.limitsState, (LimitsState state) {
String min;
String max;
if (state is LimitsLoadedSuccessfully) {
min = state.limits.min != null ? state.limits.min.toString() : null;
max = state.limits.max != null ? state.limits.max.toString() : null;
}
if (state is LimitsLoadedFailure) {
min = '0';
max = '0';
}
if (state is LimitsIsLoading) {
min = '...';
max = '...';
}
depositKey.currentState.changeLimits(min: min, max: max);
receiveKey.currentState.changeLimits(min: null, max: null);
});
depositAddressController.addListener(
() => store.depositAddress = depositAddressController.text);
depositAmountController.addListener(() {
if (depositAmountController.text != store.depositAmount) {
store.changeDepositAmount(amount: depositAmountController.text);
}
});
receiveAddressController.addListener(
() => store.receiveAddress = receiveAddressController.text);
receiveAmountController.addListener(() {
if (receiveAmountController.text != store.receiveAmount) {
store.changeReceiveAmount(amount: receiveAmountController.text);
}
});
reaction((_) => walletStore.address, (String address) {
if (store.depositCurrency == CryptoCurrency.xmr) {
depositKey.currentState.changeAddress(address: address);
}
if (store.receiveCurrency == CryptoCurrency.xmr) {
receiveKey.currentState.changeAddress(address: address);
}
});
_isReactionsSet = true;
}
void _onCurrencyChange(CryptoCurrency currency, WalletStore walletStore,
GlobalKey<ExchangeCardState> key) {
final isCurrentTypeWallet = currency == walletStore.type;
key.currentState.changeSelectedCurrency(currency);
key.currentState
.changeWalletName(isCurrentTypeWallet ? walletStore.name : null);
key.currentState
.changeAddress(address: isCurrentTypeWallet ? walletStore.address : '');
key.currentState.changeAmount(amount: '');
}
void _onWalletNameChange(WalletStore walletStore, CryptoCurrency currency,
GlobalKey<ExchangeCardState> key) {
final isCurrentTypeWallet = currency == walletStore.type;
if (isCurrentTypeWallet) {
key.currentState.changeWalletName(walletStore.name);
key.currentState.addressController.text = walletStore.address;
} else if (key.currentState.addressController.text == walletStore.address) {
key.currentState.changeWalletName(null);
key.currentState.addressController.text = null;
}
return BaseExchangeWidget(
exchangeStore: exchangeStore,
walletStore: walletStore,
exchangeTemplateStore: exchangeTemplateStore,
isTemplate: false
);
}
}
@@ -0,0 +1,55 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/stores/exchange/exchange_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/base_exchange_widget.dart';
import 'package:cake_wallet/generated/i18n.dart';
class ExchangeTemplatePage extends BasePage {
@override
String get title => S.current.exchange_new_template;
@override
Color get backgroundLightColor => Palette.darkLavender;
@override
Color get backgroundDarkColor => PaletteDark.moderateBlue;
@override
Widget trailing(BuildContext context) {
final exchangeStore = Provider.of<ExchangeStore>(context);
return PresentProviderPicker(exchangeStore: exchangeStore);
}
@override
Widget body(BuildContext context) => ExchangeTemplateForm();
}
class ExchangeTemplateForm extends StatefulWidget{
@override
ExchangeTemplateFormState createState() => ExchangeTemplateFormState();
}
class ExchangeTemplateFormState extends State<ExchangeTemplateForm> {
@override
Widget build(BuildContext context) {
final exchangeStore = Provider.of<ExchangeStore>(context);
final walletStore = Provider.of<WalletStore>(context);
final exchangeTemplateStore = Provider.of<ExchangeTemplateStore>(context);
return BaseExchangeWidget(
exchangeStore: exchangeStore,
walletStore: walletStore,
exchangeTemplateStore: exchangeTemplateStore,
isTemplate: true
);
}
}
@@ -0,0 +1,520 @@
import 'dart:ui';
import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
import 'package:cake_wallet/src/widgets/template_tile.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:mobx/mobx.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart';
import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/stores/exchange/exchange_store.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.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/src/widgets/top_panel.dart';
import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
class BaseExchangeWidget extends StatefulWidget {
BaseExchangeWidget({
@ required this.exchangeStore,
@ required this.walletStore,
@ required this.exchangeTemplateStore,
@ required this.isTemplate,
});
final ExchangeStore exchangeStore;
final WalletStore walletStore;
final ExchangeTemplateStore exchangeTemplateStore;
final bool isTemplate;
@override
BaseExchangeWidgetState createState() =>
BaseExchangeWidgetState(
exchangeStore: exchangeStore,
walletStore: walletStore,
exchangeTemplateStore: exchangeTemplateStore,
isTemplate: isTemplate
);
}
class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
BaseExchangeWidgetState({
@ required this.exchangeStore,
@ required this.walletStore,
@ required this.exchangeTemplateStore,
@ required this.isTemplate,
});
final ExchangeStore exchangeStore;
final WalletStore walletStore;
final ExchangeTemplateStore exchangeTemplateStore;
final bool isTemplate;
final depositKey = GlobalKey<ExchangeCardState>();
final receiveKey = GlobalKey<ExchangeCardState>();
final _formKey = GlobalKey<FormState>();
var _isReactionsSet = false;
@override
Widget build(BuildContext context) {
final Image arrowBottomPurple = Image.asset(
'assets/images/arrow_bottom_purple_icon.png',
color: Theme.of(context).primaryTextTheme.title.color,
height: 8,
);
final Image arrowBottomCakeGreen = Image.asset(
'assets/images/arrow_bottom_cake_green.png',
color: Theme.of(context).primaryTextTheme.title.color,
height: 8,
);
final depositWalletName =
exchangeStore.depositCurrency == CryptoCurrency.xmr
? walletStore.name
: null;
final receiveWalletName =
exchangeStore.receiveCurrency == CryptoCurrency.xmr
? walletStore.name
: null;
WidgetsBinding.instance.addPostFrameCallback(
(_) => _setReactions(context, exchangeStore, walletStore));
return Container(
color: Theme.of(context).backgroundColor,
child: Form(
key: _formKey,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24),
content: Column(
children: <Widget>[
TopPanel(
color: Theme.of(context).accentTextTheme.title.backgroundColor,
edgeInsets: EdgeInsets.only(bottom: 24),
widget: Column(
children: <Widget>[
TopPanel(
color: Theme.of(context).accentTextTheme.title.color,
widget: Observer(
builder: (_) => ExchangeCard(
key: depositKey,
title: S.of(context).you_will_send,
initialCurrency: exchangeStore.depositCurrency,
initialWalletName: depositWalletName,
initialAddress:
exchangeStore.depositCurrency == walletStore.type
? walletStore.address
: exchangeStore.depositAddress,
initialIsAmountEditable: true,
initialIsAddressEditable: exchangeStore.isDepositAddressEnabled,
isAmountEstimated: false,
currencies: CryptoCurrency.all,
onCurrencySelected: (currency) =>
exchangeStore.changeDepositCurrency(currency: currency),
imageArrow: arrowBottomPurple,
currencyButtonColor: Theme.of(context).accentTextTheme.title.color,
addressButtonsColor: Theme.of(context).accentTextTheme.title.backgroundColor,
currencyValueValidator: (value) {
exchangeStore.validateCryptoCurrency(value);
return exchangeStore.errorMessage;
},
addressTextFieldValidator: (value) {
exchangeStore.validateAddress(value,
cryptoCurrency: exchangeStore.depositCurrency);
return exchangeStore.errorMessage;
},
),
)
),
Padding(
padding: EdgeInsets.only(top: 32, left: 24, right: 24),
child: Observer(
builder: (_) => ExchangeCard(
key: receiveKey,
title: S.of(context).you_will_get,
initialCurrency: exchangeStore.receiveCurrency,
initialWalletName: receiveWalletName,
initialAddress:
exchangeStore.receiveCurrency == walletStore.type
? walletStore.address
: exchangeStore.receiveAddress,
initialIsAmountEditable: false,
initialIsAddressEditable: exchangeStore.isReceiveAddressEnabled,
isAmountEstimated: true,
currencies: CryptoCurrency.all,
onCurrencySelected: (currency) => exchangeStore
.changeReceiveCurrency(currency: currency),
imageArrow: arrowBottomCakeGreen,
currencyButtonColor: Theme.of(context).accentTextTheme.title.backgroundColor,
addressButtonsColor: Theme.of(context).accentTextTheme.title.color,
currencyValueValidator: (value) {
exchangeStore.validateCryptoCurrency(value);
return exchangeStore.errorMessage;
},
addressTextFieldValidator: (value) {
exchangeStore.validateAddress(value,
cryptoCurrency: exchangeStore.receiveCurrency);
return exchangeStore.errorMessage;
},
)),
)
],
)
),
isTemplate
? Offstage()
: Padding(
padding: EdgeInsets.only(
top: 32,
left: 24,
bottom: 24
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).send_templates,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.caption.color
),
)
],
),
),
isTemplate
? Offstage()
: Container(
height: 40,
width: double.infinity,
padding: EdgeInsets.only(left: 24),
child: Observer(
builder: (_) {
final itemCount = exchangeTemplateStore.templates.length + 1;
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: itemCount,
itemBuilder: (context, index) {
if (index == 0) {
return GestureDetector(
onTap: () => Navigator.of(context)
.pushNamed(Routes.exchangeTemplate),
child: Container(
padding: EdgeInsets.only(right: 10),
child: DottedBorder(
borderType: BorderType.RRect,
dashPattern: [8, 4],
color: Theme.of(context).accentTextTheme.title.backgroundColor,
strokeWidth: 2,
radius: Radius.circular(20),
child: Container(
height: 40,
width: 75,
padding: EdgeInsets.only(left: 10, right: 10),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: Colors.transparent,
),
child: Text(
S.of(context).send_new,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.caption.color
),
),
)
),
),
);
}
index -= 1;
final template = exchangeTemplateStore.templates[index];
return TemplateTile(
amount: template.amount,
from: template.depositCurrency,
to: template.receiveCurrency,
onTap: () {
applyTemplate(exchangeStore, template);
}
);
}
);
}
),
)
],
),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Column(children: <Widget>[
Padding(
padding: EdgeInsets.only(bottom: 15),
child: Observer(builder: (_) {
final description =
exchangeStore.provider is XMRTOExchangeProvider
? S.of(context).amount_is_guaranteed
: S.of(context).amount_is_estimate;
return Center(
child: Text(
description,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 12
),
),
);
}),
),
isTemplate
? PrimaryButton(
onPressed: () {
if (_formKey.currentState.validate()) {
exchangeTemplateStore.addTemplate(
amount: exchangeStore.depositAmount,
depositCurrency: exchangeStore.depositCurrency.toString(),
receiveCurrency: exchangeStore.receiveCurrency.toString(),
provider: exchangeStore.provider.toString(),
depositAddress: exchangeStore.depositAddress,
receiveAddress: exchangeStore.receiveAddress
);
exchangeTemplateStore.update();
Navigator.of(context).pop();
}
},
text: S.of(context).save,
color: Colors.green,
textColor: Colors.white
)
: Observer(
builder: (_) => LoadingPrimaryButton(
text: S.of(context).exchange,
onPressed: () {
if (_formKey.currentState.validate()) {
exchangeStore.createTrade();
}
},
color: Colors.blue,
textColor: Colors.white,
isLoading: exchangeStore.tradeState is TradeIsCreating,
)),
]),
)),
);
}
void applyTemplate(ExchangeStore store, ExchangeTemplate template) {
store.changeDepositCurrency(currency: CryptoCurrency.fromString(template.depositCurrency));
store.changeReceiveCurrency(currency: CryptoCurrency.fromString(template.receiveCurrency));
switch (template.provider) {
case 'XMR.TO':
store.changeProvider(provider: store.providerList[0]);
break;
case 'ChangeNOW':
store.changeProvider(provider: store.providerList[1]);
break;
case 'MorphToken':
store.changeProvider(provider: store.providerList[2]);
break;
}
store.changeDepositAmount(amount: template.amount);
store.depositAddress = template.depositAddress;
store.receiveAddress = template.receiveAddress;
}
void _setReactions(
BuildContext context, ExchangeStore store, WalletStore walletStore) {
if (_isReactionsSet) {
return;
}
final depositAddressController = depositKey.currentState.addressController;
final depositAmountController = depositKey.currentState.amountController;
final receiveAddressController = receiveKey.currentState.addressController;
final receiveAmountController = receiveKey.currentState.amountController;
final limitsState = store.limitsState;
if (limitsState is LimitsLoadedSuccessfully) {
final min = limitsState.limits.min != null
? limitsState.limits.min.toString()
: null;
final max = limitsState.limits.max != null
? limitsState.limits.max.toString()
: null;
final key = depositKey;
key.currentState.changeLimits(min: min, max: max);
}
_onCurrencyChange(store.receiveCurrency, walletStore, receiveKey);
_onCurrencyChange(store.depositCurrency, walletStore, depositKey);
reaction(
(_) => walletStore.name,
(String _) => _onWalletNameChange(
walletStore, store.receiveCurrency, receiveKey));
reaction(
(_) => walletStore.name,
(String _) => _onWalletNameChange(
walletStore, store.depositCurrency, depositKey));
reaction(
(_) => store.receiveCurrency,
(CryptoCurrency currency) =>
_onCurrencyChange(currency, walletStore, receiveKey));
reaction(
(_) => store.depositCurrency,
(CryptoCurrency currency) =>
_onCurrencyChange(currency, walletStore, depositKey));
reaction((_) => store.depositAmount, (String amount) {
if (depositKey.currentState.amountController.text != amount) {
depositKey.currentState.amountController.text = amount;
}
});
reaction((_) => store.depositAddress, (String address) {
if (depositKey.currentState.addressController.text != address) {
depositKey.currentState.addressController.text = address;
}
});
reaction((_) => store.isDepositAddressEnabled, (bool isEnabled) {
depositKey.currentState.isAddressEditable(isEditable: isEnabled);
});
reaction((_) => store.receiveAmount, (String amount) {
if (receiveKey.currentState.amountController.text !=
store.receiveAmount) {
receiveKey.currentState.amountController.text = amount;
}
});
reaction((_) => store.receiveAddress, (String address) {
if (receiveKey.currentState.addressController.text != address) {
receiveKey.currentState.addressController.text = address;
}
});
reaction((_) => store.isReceiveAddressEnabled, (bool isEnabled) {
receiveKey.currentState.isAddressEditable(isEditable: isEnabled);
});
reaction((_) => store.tradeState, (ExchangeTradeState state) {
if (state is TradeIsCreatedFailure) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
});
}
if (state is TradeIsCreatedSuccessfully) {
Navigator.of(context)
.pushNamed(Routes.exchangeConfirm, arguments: state.trade);
}
});
reaction((_) => store.limitsState, (LimitsState state) {
String min;
String max;
if (state is LimitsLoadedSuccessfully) {
min = state.limits.min != null ? state.limits.min.toString() : null;
max = state.limits.max != null ? state.limits.max.toString() : null;
}
if (state is LimitsLoadedFailure) {
min = '0';
max = '0';
}
if (state is LimitsIsLoading) {
min = '...';
max = '...';
}
depositKey.currentState.changeLimits(min: min, max: max);
receiveKey.currentState.changeLimits(min: null, max: null);
});
depositAddressController.addListener(
() => store.depositAddress = depositAddressController.text);
depositAmountController.addListener(() {
if (depositAmountController.text != store.depositAmount) {
store.changeDepositAmount(amount: depositAmountController.text);
}
});
receiveAddressController.addListener(
() => store.receiveAddress = receiveAddressController.text);
receiveAmountController.addListener(() {
if (receiveAmountController.text != store.receiveAmount) {
store.changeReceiveAmount(amount: receiveAmountController.text);
}
});
reaction((_) => walletStore.address, (String address) {
if (store.depositCurrency == CryptoCurrency.xmr) {
depositKey.currentState.changeAddress(address: address);
}
if (store.receiveCurrency == CryptoCurrency.xmr) {
receiveKey.currentState.changeAddress(address: address);
}
});
_isReactionsSet = true;
}
void _onCurrencyChange(CryptoCurrency currency, WalletStore walletStore,
GlobalKey<ExchangeCardState> key) {
final isCurrentTypeWallet = currency == walletStore.type;
key.currentState.changeSelectedCurrency(currency);
key.currentState
.changeWalletName(isCurrentTypeWallet ? walletStore.name : null);
key.currentState
.changeAddress(address: isCurrentTypeWallet ? walletStore.address : '');
key.currentState.changeAmount(amount: '');
}
void _onWalletNameChange(WalletStore walletStore, CryptoCurrency currency,
GlobalKey<ExchangeCardState> key) {
final isCurrentTypeWallet = currency == walletStore.type;
if (isCurrentTypeWallet) {
key.currentState.changeWalletName(walletStore.name);
key.currentState.addressController.text = walletStore.address;
} else if (key.currentState.addressController.text == walletStore.address) {
key.currentState.changeWalletName(null);
key.currentState.addressController.text = null;
}
}
}
@@ -0,0 +1,119 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
class CurrencyPicker extends StatelessWidget {
CurrencyPicker({
@required this.selectedAtIndex,
@required this.items,
@required this.title,
@required this.onItemSelected,
});
final int selectedAtIndex;
final List<CryptoCurrency> items;
final String title;
final Function(CryptoCurrency) onItemSelected;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
color: Colors.transparent,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: Text(
title,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: Colors.white
),
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: GestureDetector(
onTap: () => null,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(14)),
child: Container(
height: 400,
width: 300,
color: Theme.of(context).dividerColor,
child: GridView.count(
shrinkWrap: true,
crossAxisCount: 3,
childAspectRatio: 1.25,
physics: const NeverScrollableScrollPhysics(),
crossAxisSpacing: 1,
mainAxisSpacing: 1,
children: List.generate(15, (index) {
if (index == 14) {
return Container(
color: Theme.of(context).primaryTextTheme.display1.color,
);
}
final item = items[index];
final isItemSelected = index == selectedAtIndex;
final color = isItemSelected
? Theme.of(context).accentTextTheme.subtitle.decorationColor
: Theme.of(context).primaryTextTheme.display1.color;
final textColor = isItemSelected
? Colors.blue
: Theme.of(context).primaryTextTheme.title.color;
return GestureDetector(
onTap: () {
if (onItemSelected == null) {
return;
}
Navigator.of(context).pop();
onItemSelected(item);
},
child: Container(
color: color,
child: Center(
child: Text(
item.toString(),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: textColor
),
),
),
),
);
})
),
),
),
),
)
],
),
)
),
),
),
);
}
}
@@ -1,14 +1,15 @@
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/address_text_field.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
class ExchangeCard extends StatefulWidget {
ExchangeCard(
{Key key,
this.title = '',
this.initialCurrency,
this.initialAddress,
this.initialWalletName,
@@ -18,12 +19,15 @@ class ExchangeCard extends StatefulWidget {
this.currencies,
this.onCurrencySelected,
this.imageArrow,
this.currencyButtonColor = Colors.transparent,
this.addressButtonsColor = Colors.transparent,
this.currencyValueValidator,
this.addressTextFieldValidator})
: super(key: key);
final List<CryptoCurrency> currencies;
final Function(CryptoCurrency) onCurrencySelected;
final String title;
final CryptoCurrency initialCurrency;
final String initialWalletName;
final String initialAddress;
@@ -31,6 +35,8 @@ class ExchangeCard extends StatefulWidget {
final bool initialIsAddressEditable;
final bool isAmountEstimated;
final Image imageArrow;
final Color currencyButtonColor;
final Color addressButtonsColor;
final FormFieldValidator<String> currencyValueValidator;
final FormFieldValidator<String> addressTextFieldValidator;
@@ -42,6 +48,7 @@ class ExchangeCardState extends State<ExchangeCard> {
final addressController = TextEditingController();
final amountController = TextEditingController();
String _title;
String _min;
String _max;
CryptoCurrency _selectedCurrency;
@@ -52,6 +59,7 @@ class ExchangeCardState extends State<ExchangeCard> {
@override
void initState() {
_title = widget.title;
_isAmountEditable = widget.initialIsAmountEditable;
_isAddressEditable = widget.initialIsAddressEditable;
_walletName = widget.initialWalletName;
@@ -103,157 +111,114 @@ class ExchangeCardState extends State<ExchangeCard> {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.fromLTRB(22, 15, 22, 30),
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).cardColor,
borderRadius: BorderRadius.all(Radius.circular(12))),
child: Column(children: <Widget>[
_isAmountEstimated != null && _isAmountEstimated
? Row(mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[
SizedBox(
height: 30,
child: Container(
padding: EdgeInsets.fromLTRB(10, 5, 10, 5),
decoration: BoxDecoration(
color: Palette.lightGrey,
borderRadius: BorderRadius.circular(10.0)),
child: Text(
S.of(context).estimated,
style: TextStyle(
fontSize: 14,
color: Palette.wildDarkBlue,
fontWeight: FontWeight.bold),
),
),
),
])
: Container(),
Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
height: 52,
width: 90,
color: Colors.transparent,
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
_title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.caption.color
),
)
],
),
Padding(
padding: EdgeInsets.only(top: 10),
child: Stack(
children: <Widget>[
BaseTextFormField(
controller: amountController,
enabled: _isAmountEditable,
textAlign: TextAlign.left,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: true),
inputFormatters: [
BlacklistingTextInputFormatter(
RegExp('[\\-|\\ |\\,]'))
],
hintText: '0.0000',
validator: widget.currencyValueValidator
),
Positioned(
top: 8,
right: 0,
child: Container(
height: 32,
padding: EdgeInsets.only(left: 10),
color: widget.currencyButtonColor,
child: InkWell(
onTap: () => _presentPicker(context),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(_selectedCurrency.toString(),
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
color: Theme.of(context)
.primaryTextTheme
.title
.color)),
widget.imageArrow
]),
_walletName != null
? Text(_walletName,
style: TextStyle(
fontSize: 12,
color: Palette.wildDarkBlue))
: SizedBox(),
Text(
_selectedCurrency.toString(),
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
color: Theme.of(context).primaryTextTheme.title.color)),
Padding(
padding: EdgeInsets.only(left: 5),
child: widget.imageArrow,
)
]),
),
),
SizedBox(width: 10),
Flexible(
child: Column(
children: [
TextFormField(
style: TextStyle(fontSize: 23, height: 1.21),
controller: amountController,
enabled: _isAmountEditable,
textAlign: TextAlign.right,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: true),
inputFormatters: [
BlacklistingTextInputFormatter(
RegExp('[\\-|\\ |\\,]'))
],
decoration: InputDecoration(
hintStyle: TextStyle(
color: Theme.of(context).cardTheme.color,
fontSize: 23,
height: 1.21),
hintText: '0.00000000',
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: _isAmountEditable
? Palette.deepPurple
: Theme.of(context).focusColor,
width: 1.0))),
validator: widget.currencyValueValidator),
SizedBox(height: 5),
SizedBox(
height: 15,
width: double.infinity,
child: Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
_min != null
? Text(
S.of(context).min_value(
_min, _selectedCurrency.toString()),
style: TextStyle(
fontSize: 10,
height: 1.2,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color),
)
: SizedBox(),
_min != null ? SizedBox(width: 10) : SizedBox(),
_max != null
? Text(
S.of(context).max_value(
_max, _selectedCurrency.toString()),
style: TextStyle(
fontSize: 10,
height: 1.2,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color))
: SizedBox(),
]),
),
),
],
),
),
)
],
)
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
_min != null
? Text(
S.of(context).min_value(
_min, _selectedCurrency.toString()),
style: TextStyle(
fontSize: 10,
height: 1.2,
color: Theme.of(context).primaryTextTheme.caption.color),
)
: Offstage(),
_min != null ? SizedBox(width: 10) : Offstage(),
_max != null
? Text(
S.of(context).max_value(
_max, _selectedCurrency.toString()),
style: TextStyle(
fontSize: 10,
height: 1.2,
color: Theme.of(context).primaryTextTheme.caption.color))
: Offstage(),
]),
),
SizedBox(height: 10),
AddressTextField(
controller: addressController,
isActive: _isAddressEditable,
options: _isAddressEditable
? _walletName != null
? [
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook,
AddressTextFieldOption.subaddressList
]
: [
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook,
]
: [],
validator: widget.addressTextFieldValidator,
Padding(
padding: EdgeInsets.only(top: 10),
child: AddressTextField(
controller: addressController,
isActive: _isAddressEditable,
options: _isAddressEditable
? _walletName != null
? []
: [
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook,
]
: [],
isBorderExist: false,
buttonColor: widget.addressButtonsColor,
validator: widget.addressTextFieldValidator,
),
)
]),
);
@@ -261,14 +226,14 @@ class ExchangeCardState extends State<ExchangeCard> {
void _presentPicker(BuildContext context) {
showDialog<void>(
builder: (_) => Picker(
items: widget.currencies,
builder: (_) => CurrencyPicker(
selectedAtIndex: widget.currencies.indexOf(_selectedCurrency),
items: widget.currencies,
title: S.of(context).change_currency,
onItemSelected: (CryptoCurrency item) =>
widget.onCurrencySelected != null
? widget.onCurrencySelected(item)
: null),
widget.onCurrencySelected != null
? widget.onCurrencySelected(item)
: null),
context: context);
}
}
@@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/stores/exchange/exchange_store.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
class PresentProviderPicker extends StatelessWidget {
PresentProviderPicker({@required this.exchangeStore});
final ExchangeStore exchangeStore;
@override
Widget build(BuildContext context) {
final Image arrowBottom =
Image.asset('assets/images/arrow_bottom_purple_icon.png',
color: Theme.of(context).primaryTextTheme.title.color,
height: 6);
return FlatButton(
onPressed: () => _presentProviderPicker(context),
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(S.of(context).exchange,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w400,
color: Theme.of(context).primaryTextTheme.title.color)),
Observer(
builder: (_) => Text('${exchangeStore.provider.title}',
style: TextStyle(
fontSize: 10.0,
fontWeight: FontWeight.w400,
color: Theme.of(context).primaryTextTheme.caption.color)))
],
),
SizedBox(width: 5),
Padding(
padding: EdgeInsets.only(top: 8),
child: arrowBottom,
)
],
)
);
}
void _presentProviderPicker(BuildContext context) {
final items = exchangeStore.providersForCurrentPair();
final selectedItem = items.indexOf(exchangeStore.provider);
final images = List<Image>();
for (ExchangeProvider provider in items) {
switch (provider.description) {
case ExchangeProviderDescription.xmrto:
images.add(Image.asset('assets/images/xmr_btc.png'));
break;
case ExchangeProviderDescription.changeNow:
images.add(Image.asset('assets/images/change_now.png'));
break;
case ExchangeProviderDescription.morphToken:
images.add(Image.asset('assets/images/morph_icon.png'));
break;
}
}
showDialog<void>(
builder: (_) => Picker(
items: items,
images: images,
selectedAtIndex: selectedItem,
title: S.of(context).change_exchange_provider,
onItemSelected: (ExchangeProvider provider) =>
exchangeStore.changeProvider(provider: provider)),
context: context);
}
}
@@ -17,43 +17,42 @@ class ExchangeConfirmPage extends BasePage {
@override
Widget body(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.only(left: 80.0, right: 80.0),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
S.of(context).exchange_result_write_down_trade_id,
final copyImage = Image.asset('assets/images/copy_content.png',
color: Theme.of(context).primaryTextTheme.title.color);
return Container(
padding: EdgeInsets.all(24),
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
S.of(context).exchange_result_write_down_trade_id,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color),
),
Padding(
padding: EdgeInsets.only(top: 60),
child: Text(
S.of(context).trade_id,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18.0,
color: Theme.of(context)
.primaryTextTheme
.button
.color),
fontSize: 16.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color),
),
SizedBox(
height: 70.0,
),
Text(
S.of(context).trade_id(trade.id),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18.0,
color: Theme.of(context)
.primaryTextTheme
.button
.color),
),
SizedBox(
height: 70.0,
),
PrimaryButton(
onPressed: () {
),
Padding(
padding: EdgeInsets.only(top: 24),
child: Builder(
builder: (context) => GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(text: trade.id));
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
@@ -62,31 +61,52 @@ class ExchangeConfirmPage extends BasePage {
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 1500),
));
},
text: S.of(context).copy_id,
color: Theme.of(context)
.accentTextTheme
.caption
.backgroundColor,
borderColor: Theme.of(context)
.accentTextTheme
.caption
.decorationColor)
],
),
))),
Container(
padding: EdgeInsets.only(left: 20.0, right: 20.0, bottom: 40.0),
child: PrimaryButton(
child: Container(
height: 60,
padding: EdgeInsets.only(left: 24, right: 24),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30)),
color: Theme.of(context).accentTextTheme.title.backgroundColor
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Text(
trade.id,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color
),
),
),
Padding(
padding: EdgeInsets.only(left: 12),
child: copyImage,
)
],
),
),
)
),
)
],
),
)),
PrimaryButton(
onPressed: () => Navigator.of(context)
.pushReplacementNamed(Routes.exchangeTrade, arguments: trade),
text: S.of(context).saved_the_trade_id,
color: Theme.of(context).primaryTextTheme.button.backgroundColor,
borderColor:
Theme.of(context).primaryTextTheme.button.decorationColor),
)
],
color: Colors.green,
textColor: Colors.white)
],
),
);
}
}
@@ -6,18 +6,18 @@ import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/stores/exchange_trade/exchange_trade_store.dart';
import 'package:cake_wallet/src/stores/send/send_store.dart';
import 'package:cake_wallet/src/stores/send/sending_state.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/screens/exchange_trade/widgets/copy_button.dart';
import 'package:cake_wallet/src/screens/receive/qr_image.dart';
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/exchange_trade/widgets/timer_widget.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/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
class ExchangeTradePage extends BasePage {
@override
@@ -46,281 +46,274 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
_setEffects(context);
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 20, right: 20, top: 20),
content: Observer(builder: (_) {
final trade = tradeStore.trade;
final walletName = walletStore.name;
return Container(
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 24, right: 24, top: 24),
content: Observer(builder: (_) {
final trade = tradeStore.trade;
final walletName = walletStore.name;
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).id,
style: TextStyle(
height: 2,
fontWeight: FontWeight.bold,
fontSize: 14.0,
color: Theme.of(context)
.primaryTextTheme
.button
.color),
),
Text(
'${trade.id ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).amount,
style: TextStyle(
height: 2,
fontWeight: FontWeight.bold,
fontSize: 14.0,
color: Theme.of(context)
.primaryTextTheme
.button
.color),
),
Text(
'${trade.amount ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color),
)
],
),
trade.extraId != null
? Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).payment_id,
style: TextStyle(
height: 2,
fontWeight: FontWeight.bold,
fontSize: 14.0,
color: Theme.of(context)
.primaryTextTheme
.button
.color),
),
Text(
'${trade.extraId ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color),
)
],
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).id,
style: TextStyle(
height: 2,
fontWeight: FontWeight.bold,
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.title.color),
),
Text(
'${trade.id ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: Theme.of(context).primaryTextTheme.caption.color),
)
: Container(),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).status,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme
.button
.color,
height: 2),
),
Text(
'${trade.state ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color),
)
],
),
trade.expiredAt != null
? Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).offer_expires_in,
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context)
.primaryTextTheme
.button
.color),
),
TimerWidget(trade.expiredAt,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color)
],
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).amount,
style: TextStyle(
height: 2,
fontWeight: FontWeight.bold,
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.title.color),
),
Text(
'${trade.amount ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: Theme.of(context).primaryTextTheme.caption.color),
)
: Container(),
],
),
SizedBox(width: 10),
Container(
padding: EdgeInsets.all(5),
color: Colors.white,
constraints: BoxConstraints(minWidth: 65, maxWidth: 105),
child: QrImage(
data: trade.inputAddress ?? fetchingLabel,
backgroundColor: Colors.white))
],
),
),
SizedBox(
height: 20.0,
),
Center(
child: Text(
S.of(context).trade_is_powered_by(trade.provider != null
? trade.provider.title
: fetchingLabel),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.headline.color),
),
),
Container(
padding: EdgeInsets.only(top: 20, bottom: 20),
child: Center(
child: Text(
trade.inputAddress ?? fetchingLabel,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 14.0, color: Palette.lightViolet),
],
),
trade.extraId != null
? Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).payment_id,
style: TextStyle(
height: 2,
fontWeight: FontWeight.bold,
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.title.color),
),
Text(
'${trade.extraId ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: Theme.of(context).primaryTextTheme.caption.color),
)
],
)
: Container(),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).status,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color,
height: 2),
),
Text(
'${trade.state ?? fetchingLabel}',
style: TextStyle(
fontSize: 14.0,
height: 2,
color: Theme.of(context).primaryTextTheme.caption.color),
)
],
),
trade.expiredAt != null
? Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).offer_expires_in,
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.title.color),
),
TimerWidget(trade.expiredAt,
color: Theme.of(context).primaryTextTheme.caption.color)
],
)
: Container(),
],
),
],
),
),
),
Container(
padding: EdgeInsets.only(left: 50.0, right: 50.0),
child: Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(right: 5.0),
child: CopyButton(
onPressed: () => Clipboard.setData(
ClipboardData(text: trade.inputAddress)),
text: S.of(context).copy_address,
color: Theme.of(context)
.accentTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.accentTextTheme
.button
.decorationColor),
)),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 5.0),
child: CopyButton(
onPressed: () =>
Clipboard.setData(ClipboardData(text: trade.id)),
text: S.of(context).copy_id,
color: Theme.of(context)
.accentTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.accentTextTheme
.button
.decorationColor),
))
],
Padding(
padding: EdgeInsets.only(top: 20),
child: Row(
children: <Widget>[
Spacer(
flex: 1,
),
Flexible(
flex: 1,
child: Center(
child: AspectRatio(
aspectRatio: 1.0,
child: QrImage(
data: trade.inputAddress ?? fetchingLabel,
backgroundColor: Colors.transparent,
foregroundColor: Theme.of(context).primaryTextTheme.display4.color,
),
),
)),
Spacer(
flex: 1,
)
],
),
),
),
Container(
padding: EdgeInsets.only(top: 20),
child: Text(
tradeStore.isSendable
? S.of(context).exchange_result_confirm(
trade.amount ?? fetchingLabel,
trade.from.toString(),
walletName)
: S.of(context).exchange_result_description(
trade.amount ?? fetchingLabel, trade.from.toString()),
SizedBox(
height: 20.0,
),
Center(
child: Text(
S.of(context).trade_is_powered_by(trade.provider != null
? trade.provider.title
: fetchingLabel),
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color),
),
),
Container(
padding: EdgeInsets.only(top: 20, bottom: 20),
child: Center(
child: Text(
trade.inputAddress ?? fetchingLabel,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.caption.color),
),
),
),
Container(
child: Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(right: 5.0),
child: Builder(
builder: (context) => PrimaryButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: trade.inputAddress));
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
S.of(context).copied_to_clipboard,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 1500),
));
},
text: S.of(context).copy_address,
color: Theme.of(context).accentTextTheme.title.backgroundColor,
textColor: Theme.of(context).primaryTextTheme.title.color)
),
)),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 5.0),
child: Builder(
builder: (context) => PrimaryButton(
onPressed: () {
Clipboard.setData(ClipboardData(text: trade.id));
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
S.of(context).copied_to_clipboard,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 1500),
));
},
text: S.of(context).copy_id,
color: Theme.of(context).accentTextTheme.title.backgroundColor,
textColor: Theme.of(context).primaryTextTheme.title.color)
),
))
],
),
),
Container(
padding: EdgeInsets.only(top: 20),
child: Text(
tradeStore.isSendable
? S.of(context).exchange_result_confirm(
trade.amount ?? fetchingLabel,
trade.from.toString(),
walletName)
: S.of(context).exchange_result_description(
trade.amount ?? fetchingLabel, trade.from.toString()),
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 13.0,
color: Theme.of(context).primaryTextTheme.title.color),
),
),
Text(
S.of(context).exchange_result_write_down_ID,
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 13.0,
color: Theme.of(context).primaryTextTheme.title.color),
),
),
Text(
S.of(context).exchange_result_write_down_ID,
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 13.0,
color: Theme.of(context).accentTextTheme.title.color),
)
],
);
}),
bottomSection: Observer(
builder: (_) => tradeStore.trade.from == CryptoCurrency.xmr &&
!(sendStore.state is TransactionCommitted)
? Container(
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: LoadingPrimaryButton(
isDisabled: tradeStore.trade.inputAddress == null ||
tradeStore.trade.inputAddress.isEmpty,
isLoading: sendStore.state is CreatingTransaction ||
sendStore.state is TransactionCommitted,
onPressed: () => sendStore.createTransaction(
address: tradeStore.trade.inputAddress,
amount: tradeStore.trade.amount),
text: tradeStore.trade.provider ==
ExchangeProviderDescription.xmrto
? S.of(context).confirm
: S.of(context).send_xmr,
color: Theme.of(context)
.primaryTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.primaryTextTheme
.button
.decorationColor),
)
: Offstage()),
)
],
);
}),
bottomSectionPadding: EdgeInsets.all(24),
bottomSection: Observer(
builder: (_) => tradeStore.trade.from == CryptoCurrency.xmr &&
!(sendStore.state is TransactionCommitted)
? LoadingPrimaryButton(
isDisabled: tradeStore.trade.inputAddress == null ||
tradeStore.trade.inputAddress.isEmpty,
isLoading: sendStore.state is CreatingTransaction ||
sendStore.state is TransactionCommitted,
onPressed: () => sendStore.createTransaction(
address: tradeStore.trade.inputAddress,
amount: tradeStore.trade.amount),
text: tradeStore.trade.provider ==
ExchangeProviderDescription.xmrto
? S.of(context).confirm
: S.of(context).send_xmr,
color: Colors.blue,
textColor: Colors.white)
: Offstage()),
),
);
}
@@ -337,14 +330,11 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).error),
content: Text(state.error),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () => Navigator.of(context).pop())
],
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
});
@@ -355,23 +345,18 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).confirm_sending),
content: Text(S.of(context).commit_transaction_amount_fee(
sendStore.pendingTransaction.amount,
sendStore.pendingTransaction.fee)),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () {
Navigator.of(context).pop();
sendStore.commitTransaction();
}),
FlatButton(
child: Text(S.of(context).cancel),
onPressed: () => Navigator.of(context).pop(),
)
],
return AlertWithTwoActions(
alertTitle: S.of(context).confirm_sending,
alertContent: S.of(context).commit_transaction_amount_fee(
sendStore.pendingTransaction.amount,
sendStore.pendingTransaction.fee),
leftButtonText: S.of(context).ok,
rightButtonText: S.of(context).cancel,
actionLeftButton: () {
Navigator.of(context).pop();
sendStore.commitTransaction();
},
actionRightButton: () => Navigator.of(context).pop()
);
});
});
@@ -382,14 +367,11 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).sending),
content: Text(S.of(context).transaction_sent),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () => Navigator.of(context).pop())
],
return AlertWithOneAction(
alertTitle: S.of(context).sending,
alertContent: S.of(context).transaction_sent,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
});
@@ -1,30 +0,0 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class CopyButton extends StatelessWidget {
const CopyButton(
{@required this.onPressed,
@required this.text,
@required this.color,
@required this.borderColor});
final VoidCallback onPressed;
final Color color;
final Color borderColor;
final String text;
@override
Widget build(BuildContext context) {
return ButtonTheme(
minWidth: double.infinity,
height: 44.0,
child: FlatButton(
onPressed: onPressed,
color: color,
shape: RoundedRectangleBorder(
side: BorderSide(color: borderColor),
borderRadius: BorderRadius.circular(10.0)),
child: Text(text, style: TextStyle(fontSize: 14.0)),
));
}
}
+110 -29
View File
@@ -11,43 +11,124 @@ class FaqPage extends BasePage {
String get title => S.current.faq;
@override
Widget body(BuildContext context) {
return FutureBuilder(
builder: (context, snapshot) {
final faqItems = jsonDecode(snapshot.data.toString()) as List;
Widget body(BuildContext context) => FaqForm();
}
return ListView.separated(
itemBuilder: (BuildContext context, int index) {
final itemTitle = faqItems[index]["question"].toString();
final itemChild = faqItems[index]["answer"].toString();
class FaqForm extends StatefulWidget {
@override
FaqFormState createState() => FaqFormState();
}
return ExpansionTile(
title: Text(itemTitle),
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.only(left: 15.0, right: 15.0),
class FaqFormState extends State<FaqForm> {
List<Icon> icons;
List<Color> colors;
bool isLoaded = false;
@override
Widget build(BuildContext context) {
final addIcon = Icon(Icons.add, color: Theme.of(context).primaryTextTheme.title.color);
final removeIcon = Icon(Icons.remove, color: Colors.green);
return Container(
padding: EdgeInsets.only(top: 12),
child: Container(
color: Theme.of(context).accentTextTheme.headline.color,
child: FutureBuilder(
builder: (context, snapshot) {
final faqItems = jsonDecode(snapshot.data.toString()) as List;
if (snapshot.hasData) {
setIconsAndColors(context, faqItems.length, addIcon);
}
return SingleChildScrollView(
child: ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
final itemTitle = faqItems[index]["question"].toString();
final itemChild = faqItems[index]["answer"].toString();
return ExpansionTile(
title: Padding(
padding: EdgeInsets.only(left: 8, top: 12, bottom: 12),
child: Text(
itemChild,
itemTitle,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: colors[index]
),
),
))
],
)
],
),
trailing: Padding(
padding: EdgeInsets.only(right: 24),
child: Container(
width: double.minPositive,
child: Center(
child: icons[index]
),
),
),
backgroundColor: Theme.of(context).accentTextTheme.headline.backgroundColor,
onExpansionChanged: (value) {
setState(() {
if (value) {
icons[index] = removeIcon;
colors[index] = Colors.green;
} else {
icons[index] = addIcon;
colors[index] = Theme.of(context).primaryTextTheme.title.color;
}
});
},
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.only(
left: 24.0,
right: 24.0,
bottom: 8
),
child: Text(
itemChild,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).primaryTextTheme.title.color
),
),
))
],
)
],
);
},
separatorBuilder: (_, __) =>
Container(color: Theme.of(context).dividerColor, height: 1.0),
itemCount: faqItems == null ? 0 : faqItems.length,
),
);
},
separatorBuilder: (_, __) =>
Divider(color: Theme.of(context).dividerTheme.color, height: 1.0),
itemCount: faqItems == null ? 0 : faqItems.length,
);
},
future: rootBundle.loadString(getFaqPath(context)),
future: rootBundle.loadString(getFaqPath(context)),
),
),
);
}
void setIconsAndColors(BuildContext context, int index, Icon icon) {
if (isLoaded) {
return;
}
icons = List.generate(index, (int i) => icon);
colors = List.generate(index, (int i) => Theme.of(context).primaryTextTheme.title.color);
isLoaded = true;
}
String getFaqPath(BuildContext context) {
final settingsStore = Provider.of<SettingsStore>(context);
@@ -80,4 +161,4 @@ class FaqPage extends BasePage {
return 'assets/faq/faq_en.json';
}
}
}
}
+124 -70
View File
@@ -12,9 +12,10 @@ import 'package:cake_wallet/src/domain/services/wallet_service.dart';
import 'package:cake_wallet/src/screens/base_page.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/palette.dart';
import 'package:cake_wallet/src/stores/seed_language/seed_language_store.dart';
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
class NewWalletPage extends BasePage {
NewWalletPage(
@@ -39,14 +40,42 @@ class WalletNameForm extends StatefulWidget {
}
class _WalletNameFormState extends State<WalletNameForm> {
static const aspectRatioImage = 1.22;
final _formKey = GlobalKey<FormState>();
final nameController = TextEditingController();
final walletNameImage = Image.asset('assets/images/wallet_name.png');
@override
void dispose() {
nameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final walletCreationStore = Provider.of<WalletCreationStore>(context);
final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
final List<String> seedLocales = [
S.current.seed_language_english,
S.current.seed_language_chinese,
S.current.seed_language_dutch,
S.current.seed_language_german,
S.current.seed_language_japanese,
S.current.seed_language_portuguese,
S.current.seed_language_russian,
S.current.seed_language_spanish
];
nameController.addListener(() {
if (nameController.text.isNotEmpty) {
walletCreationStore.setDisabledStatus(false);
} else {
walletCreationStore.setDisabledStatus(true);
}
});
reaction((_) => walletCreationStore.state, (WalletCreationState state) {
if (state is WalletCreatedSuccessfully) {
Navigator.of(context).popUntil((route) => route.isFirst);
@@ -56,81 +85,106 @@ class _WalletNameFormState extends State<WalletNameForm> {
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text(state.error),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () => Navigator.of(context).pop(),
),
],
builder: (_) {
return AlertWithOneAction(
alertTitle: S.current.new_wallet,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
});
}
});
return ScrollableWithBottomSection(
content: Column(children: [
Padding(
padding: EdgeInsets.only(bottom: 10),
child: Image.asset('assets/images/bitmap.png',
height: 224, width: 400),
),
Padding(
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
child: Form(
key: _formKey,
child: TextFormField(
style: TextStyle(
fontSize: 24.0,
color: Theme.of(context).accentTextTheme.subtitle.color),
controller: nameController,
decoration: InputDecoration(
hintStyle: TextStyle(
fontSize: 24.0, color: Theme.of(context).hintColor),
hintText: S.of(context).wallet_name,
focusedBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
width: 1.0))),
validator: (value) {
walletCreationStore.validateWalletName(value);
return walletCreationStore.errorMessage;
},
)),
),
Padding(padding: EdgeInsets.only(bottom: 20),
child: Text(
S.of(context).seed_language_choose,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16.0),
return Container(
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: walletNameImage, fit: BoxFit.fill)),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: Form(
key: _formKey,
child: TextFormField(
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color),
controller: nameController,
decoration: InputDecoration(
hintStyle: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.caption.color),
hintText: S.of(context).wallet_name,
focusedBorder: UnderlineInputBorder(
borderSide:
BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
validator: (value) {
walletCreationStore.validateWalletName(value);
return walletCreationStore.errorMessage;
},
)),
),
),
Padding(padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
child: SeedLanguagePicker(),
)
]),
bottomSection: Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: () {
if (_formKey.currentState.validate()) {
walletCreationStore.create(name: nameController.text,
language: seedLanguageStore.selectedSeedLanguage);
}
},
text: S.of(context).continue_text,
color: Theme.of(context).primaryTextTheme.button.backgroundColor,
borderColor:
Theme.of(context).primaryTextTheme.button.decorationColor,
isLoading: walletCreationStore.state is WalletIsCreating,
);
},
));
Padding(padding: EdgeInsets.only(top: 40),
child: Text(
S.of(context).seed_language_choose,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color
),
),
),
Padding(padding: EdgeInsets.only(top: 24),
child: Observer(
builder: (_) => SelectButton(
image: null,
text: seedLocales[seedLanguages.indexOf(seedLanguageStore.selectedSeedLanguage)],
color: Theme.of(context).accentTextTheme.title.backgroundColor,
textColor: Theme.of(context).primaryTextTheme.title.color,
onTap: () async => await showDialog(
context: context,
builder: (BuildContext context) => SeedLanguagePicker()
)
)
),
)
]),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: () {
if (_formKey.currentState.validate()) {
walletCreationStore.create(name: nameController.text,
language: seedLanguageStore.selectedSeedLanguage);
}
},
text: S.of(context).continue_text,
color: Colors.green,
textColor: Colors.white,
isLoading: walletCreationStore.state is WalletIsCreating,
isDisabled: walletCreationStore.isDisabledStatus,
);
},
)),
);
}
}
@@ -0,0 +1,145 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.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/src/screens/new_wallet/widgets/select_button.dart';
import 'package:cake_wallet/routes.dart';
class NewWalletTypePage extends BasePage {
@override
String get title => S.current.new_wallet;
@override
Widget body(BuildContext context) => WalletTypeForm();
}
class WalletTypeForm extends StatefulWidget {
@override
WalletTypeFormState createState() => WalletTypeFormState();
}
class WalletTypeFormState extends State<WalletTypeForm> {
static const aspectRatioImage = 1.22;
final moneroIcon = Image.asset('assets/images/monero.png', height: 24, width: 24);
final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
bool isDisabledButton;
bool isMoneroSelected;
bool isBitcoinSelected;
Color moneroBackgroundColor = Colors.transparent;
Color moneroTextColor = Colors.transparent;
Color bitcoinBackgroundColor = Colors.transparent;
Color bitcoinTextColor = Colors.transparent;
@override
void initState() {
isDisabledButton = true;
isMoneroSelected = false;
isBitcoinSelected = false;
super.initState();
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
}
void afterLayout(dynamic _) {
moneroBackgroundColor = Theme.of(context).accentTextTheme.title.backgroundColor;
moneroTextColor = Theme.of(context).primaryTextTheme.title.color;
bitcoinBackgroundColor = Theme.of(context).accentTextTheme.title.backgroundColor;
bitcoinTextColor = Theme.of(context).primaryTextTheme.title.color;
setState(() {});
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 24),
child: 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: walletTypeImage, 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.w600,
color: Theme.of(context).primaryTextTheme.title.color
),
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: SelectButton(
image: bitcoinIcon,
text: 'Bitcoin',
color: bitcoinBackgroundColor,
textColor: bitcoinTextColor,
onTap: () {}),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: SelectButton(
image: moneroIcon,
text: 'Monero',
color: moneroBackgroundColor,
textColor: moneroTextColor,
onTap: () => onSelectMoneroButton(context)),
)
],
),
bottomSectionPadding: EdgeInsets.only(
left: 24,
right: 24,
bottom: 24
),
bottomSection: PrimaryButton(
onPressed: () => Navigator.of(context).pushNamed(Routes.newWallet),
text: S.of(context).seed_language_next,
color: Colors.green,
textColor: Colors.white,
isDisabled: isDisabledButton,
),
),
);
}
void onSelectMoneroButton(BuildContext context) {
isMoneroSelected = true;
isBitcoinSelected = false;
isDisabledButton = false;
moneroBackgroundColor = Theme.of(context).accentTextTheme.title.decorationColor;
moneroTextColor = Theme.of(context).primaryTextTheme.title.backgroundColor;
bitcoinBackgroundColor = Theme.of(context).accentTextTheme.title.backgroundColor;
bitcoinTextColor = Theme.of(context).primaryTextTheme.title.color;
setState(() {});
}
void onSelectBitcoinButton(BuildContext context) {
isMoneroSelected = false;
isBitcoinSelected = true;
isDisabledButton = false;
moneroBackgroundColor = Theme.of(context).accentTextTheme.title.backgroundColor;
moneroTextColor = Theme.of(context).primaryTextTheme.title.color;
bitcoinBackgroundColor = moneroBackgroundColor = Theme.of(context).accentTextTheme.title.decorationColor;
bitcoinTextColor = Theme.of(context).primaryTextTheme.title.backgroundColor;
setState(() {});
}
}
@@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
class SelectButton extends StatelessWidget {
SelectButton({
@required this.image,
@required this.text,
@required this.color,
@required this.textColor,
@required this.onTap,
});
final Image image;
final String text;
final Color color;
final Color textColor;
final VoidCallback onTap;
final selectArrowImage = Image.asset('assets/images/select_arrow.png');
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
width: double.infinity,
height: 60,
padding: EdgeInsets.only(left: 30, right: 30),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(30)),
color: color
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
image != null
? image
: Offstage(),
Padding(
padding: image != null
? EdgeInsets.only(left: 15)
: EdgeInsets.only(left: 0),
child: Text(
text,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: textColor
),
),
)
],
),
selectArrowImage
],
),
),
);
}
}
+169 -143
View File
@@ -1,11 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/stores/node_list/node_list_store.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
class NewNodePage extends BasePage {
@override
@@ -36,130 +37,166 @@ class NewNodeFormState extends State<NewNodePageForm> {
super.dispose();
}
void onHandleControllers(NodeListStore nodeListStore) {
if (_nodeAddressController.text.isNotEmpty &&
_nodePortController.text.isNotEmpty) {
nodeListStore.setDisabledState(false);
} else {
nodeListStore.setDisabledState(true);
}
}
@override
Widget build(BuildContext context) {
final nodeList = Provider.of<NodeListStore>(context);
return Form(
key: _formKey,
child: Column(
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.only(left: 38.0, right: 38.0, top: 0),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: TextFormField(
style: TextStyle(fontSize: 14.0),
decoration: InputDecoration(
hintStyle:
TextStyle(color: Palette.wildDarkBlue),
hintText: S.of(context).node_address,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
width: 1.0))),
controller: _nodeAddressController,
validator: (value) {
nodeList.validateNodeAddress(value);
return nodeList.errorMessage;
},
),
)
],
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
Expanded(
child: TextFormField(
style: TextStyle(fontSize: 14.0),
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: false),
decoration: InputDecoration(
hintStyle:
TextStyle(color: Palette.wildDarkBlue),
hintText: S.of(context).node_port,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
width: 1.0))),
controller: _nodePortController,
validator: (value) {
nodeList.validateNodePort(value);
return nodeList.errorMessage;
},
),
)
],
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
Expanded(
child: TextFormField(
style: TextStyle(fontSize: 14.0),
decoration: InputDecoration(
hintStyle:
TextStyle(color: Palette.wildDarkBlue),
hintText: S.of(context).login,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
width: 1.0))),
controller: _loginController,
validator: (value) => null,
),
)
],
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
Expanded(
child: TextFormField(
style: TextStyle(fontSize: 14.0),
decoration: InputDecoration(
hintStyle:
TextStyle(color: Palette.wildDarkBlue),
hintText: S.of(context).password,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
width: 1.0))),
controller: _passwordController,
validator: (value) => null,
),
)
],
)
],
),
),
)),
Container(
padding: EdgeInsets.only(bottom: 20.0, left: 20.0, right: 20.0),
child: Row(
_nodeAddressController.addListener(() {onHandleControllers(nodeList);});
_nodePortController.addListener(() {onHandleControllers(nodeList);});
return Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24.0),
content: Form(
key: _formKey,
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Flexible(
child: Container(
Expanded(
child: TextFormField(
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
decoration: InputDecoration(
hintStyle:
TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: S.of(context).node_address,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
controller: _nodeAddressController,
validator: (value) {
nodeList.validateNodeAddress(value);
return nodeList.errorMessage;
},
),
)
],
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
Expanded(
child: TextFormField(
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: false),
decoration: InputDecoration(
hintStyle:
TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: S.of(context).node_port,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
controller: _nodePortController,
validator: (value) {
nodeList.validateNodePort(value);
return nodeList.errorMessage;
},
),
)
],
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
Expanded(
child: TextFormField(
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
decoration: InputDecoration(
hintStyle:
TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: S.of(context).login,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
controller: _loginController,
validator: (value) => null,
),
)
],
),
SizedBox(height: 10.0),
Row(
children: <Widget>[
Expanded(
child: TextFormField(
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
decoration: InputDecoration(
hintStyle:
TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: S.of(context).password,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
controller: _passwordController,
validator: (value) => null,
),
)
],
)
],
)
),
bottomSectionPadding: EdgeInsets.only(bottom: 24),
bottomSection: Observer(
builder: (_) => Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(right: 8.0),
child: PrimaryButton(
onPressed: () {
@@ -169,17 +206,11 @@ class NewNodeFormState extends State<NewNodePageForm> {
_passwordController.text = '';
},
text: S.of(context).reset,
color: Theme.of(context)
.accentTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.accentTextTheme
.button
.decorationColor),
color: Colors.red,
textColor: Colors.white),
)),
Flexible(
child: Container(
Flexible(
child: Container(
padding: EdgeInsets.only(left: 8.0),
child: PrimaryButton(
onPressed: () async {
@@ -196,20 +227,15 @@ class NewNodeFormState extends State<NewNodePageForm> {
Navigator.of(context).pop();
},
text: S.of(context).save,
color: Theme.of(context)
.primaryTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.primaryTextTheme
.button
.decorationColor,
color: Colors.green,
textColor: Colors.white,
isDisabled: nodeList.disabledState,
),
)),
],
),
)
],
));
],
)
),
)
);
}
}
+163 -185
View File
@@ -5,10 +5,12 @@ import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/nodes/node_indicator.dart';
import 'package:cake_wallet/src/screens/nodes/widgets/node_indicator.dart';
import 'package:cake_wallet/src/stores/node_list/node_list_store.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/nodes/widgets/node_list_row.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
class NodeListPage extends BasePage {
NodeListPage();
@@ -21,71 +23,42 @@ class NodeListPage extends BasePage {
final nodeList = Provider.of<NodeListStore>(context);
final settings = Provider.of<SettingsStore>(context);
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
S.of(context).node_reset_settings_title,
textAlign: TextAlign.center,
),
content: Text(
S.of(context).nodes_list_reset_to_default_message,
textAlign: TextAlign.center,
),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(S.of(context).cancel)),
FlatButton(
onPressed: () async {
Navigator.pop(context);
await nodeList.reset();
await settings.setCurrentNodeToDefault();
},
child: Text(S.of(context).reset))
],
);
});
},
child: Text(
S.of(context).reset,
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.subtitle.color),
)),
),
Container(
width: 28.0,
height: 28.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).selectedRowColor),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Icon(Icons.add, color: Palette.violet, size: 22.0),
ButtonTheme(
minWidth: 28.0,
height: 28.0,
child: FlatButton(
shape: CircleBorder(),
onPressed: () async =>
await Navigator.of(context).pushNamed(Routes.newNode),
child: Offstage()),
)
],
return Container(
height: 32,
width: 72,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(16)),
color: Theme.of(context).accentTextTheme.title.backgroundColor
),
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
onPressed: () async {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).node_reset_settings_title,
alertContent: S.of(context).nodes_list_reset_to_default_message,
leftButtonText: S.of(context).reset,
rightButtonText: S.of(context).cancel,
actionLeftButton: () async {
Navigator.of(context).pop();
await nodeList.reset();
await settings.setCurrentNodeToDefault();
},
actionRightButton: () => Navigator.of(context).pop()
);
});
},
child: Text(
S.of(context).reset,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 10.0,
color: Colors.blue),
)),
],
),
);
}
@@ -104,137 +77,142 @@ class NodeListPageBodyState extends State<NodeListPageBody> {
final nodeList = Provider.of<NodeListStore>(context);
final settings = Provider.of<SettingsStore>(context);
final currentColor = Theme.of(context).selectedRowColor;
final notCurrentColor = Theme.of(context).backgroundColor;
final trashImage = Image.asset('assets/images/trash.png', height: 32, width: 32, color: Colors.white);
final currentColor = Theme.of(context).accentTextTheme.subtitle.decorationColor;
final notCurrentColor = Theme.of(context).accentTextTheme.title.backgroundColor;
final currentTextColor = Colors.blue;
final notCurrentTextColor = Theme.of(context).primaryTextTheme.title.color;
return Container(
padding: EdgeInsets.only(bottom: 20.0),
height: double.infinity,
padding: EdgeInsets.only(top: 12),
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(child: Observer(builder: (context) {
return ListView.separated(
separatorBuilder: (_, __) => Divider(
color: Theme.of(context).dividerTheme.color, height: 1),
itemCount: nodeList.nodes.length,
itemBuilder: (BuildContext context, int index) {
final node = nodeList.nodes[index];
NodeListRow(
title: S.of(context).add_new_node,
trailing: Icon(Icons.add,
color: Theme.of(context).primaryTextTheme.title.color,
size: 24.0),
color: Theme.of(context).accentTextTheme.title.backgroundColor,
textColor: Theme.of(context).primaryTextTheme.title.color,
onTap: () async =>
await Navigator.of(context).pushNamed(Routes.newNode),
isDrawTop: true,
isDrawBottom: true),
Expanded(
child: Padding(
padding: EdgeInsets.only(top: 32),
child: Observer(
builder: (_) => ListView.separated(
separatorBuilder: (_, __) => Container(
height: 1,
padding: EdgeInsets.only(left: 24),
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: Container(
height: 1,
color: Theme.of(context).dividerColor,
),
),
itemCount: nodeList.nodes.length,
itemBuilder: (BuildContext context, int index) {
final node = nodeList.nodes[index];
return Observer(builder: (_) {
final isCurrent = settings.node == null
? false
: node.key == settings.node.key;
final isDrawTop = index == 0 ? true : false;
final isDrawBottom = index == nodeList.nodes.length - 1 ? true : false;
final content = Container(
color: isCurrent ? currentColor : notCurrentColor,
child: ListTile(
title: Text(
node.uri,
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
),
trailing: FutureBuilder(
future: nodeList.isNodeOnline(node),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
return NodeIndicator(
color: snapshot.data as bool
? Palette.green
: Palette.red);
default:
return NodeIndicator();
}
}),
onTap: () async {
if (!isCurrent) {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text(
S
.of(context)
.change_current_node(node.uri),
textAlign: TextAlign.center,
),
actions: <Widget>[
FlatButton(
onPressed: () =>
Navigator.pop(context),
child: Text(S.of(context).cancel)),
FlatButton(
onPressed: () async {
return Observer(
builder: (_) {
final isCurrent = settings.node == null
? false
: node.key == settings.node.key;
final content = NodeListRow(
title: node.uri,
trailing: FutureBuilder(
future: nodeList.isNodeOnline(node),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
return NodeIndicator(
color: snapshot.data as bool
? Palette.green
: Palette.red);
default:
return NodeIndicator();
}
}),
color: isCurrent ? currentColor : notCurrentColor,
textColor: isCurrent ? currentTextColor : notCurrentTextColor,
onTap: () async {
if (!isCurrent) {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.current.nodes,
alertContent: S.of(context)
.change_current_node(node.uri),
leftButtonText: S.of(context).change,
rightButtonText: S.of(context).cancel,
actionLeftButton: () async {
Navigator.of(context).pop();
await settings.setCurrentNode(
node: node);
},
child: Text(S.of(context).change)),
],
);
});
}
},
));
actionRightButton: () => Navigator.of(context).pop()
);
});
}
},
isDrawTop: isDrawTop,
isDrawBottom: isDrawBottom);
return isCurrent
? content
: Dismissible(
key: Key('${node.key}'),
confirmDismiss: (direction) async {
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
S.of(context).remove_node,
textAlign: TextAlign.center,
),
content: Text(
S.of(context).remove_node_message,
textAlign: TextAlign.center,
),
actions: <Widget>[
FlatButton(
onPressed: () =>
Navigator.pop(context, false),
child: Text(S.of(context).cancel)),
FlatButton(
onPressed: () =>
Navigator.pop(context, true),
child: Text(S.of(context).remove)),
],
);
});
},
onDismissed: (direction) async =>
await nodeList.remove(node: node),
direction: DismissDirection.endToStart,
background: Container(
padding: EdgeInsets.only(right: 10.0),
alignment: AlignmentDirectional.centerEnd,
color: Palette.red,
child: Column(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
const Icon(
CupertinoIcons.delete,
color: Colors.white,
),
Text(
S.of(context).delete,
style: TextStyle(color: Colors.white),
)
],
)),
child: content);
});
});
}))
return isCurrent
? content
: Dismissible(
key: Key('${node.key}'),
confirmDismiss: (direction) async {
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).remove_node,
alertContent: S.of(context).remove_node_message,
leftButtonText: S.of(context).remove,
rightButtonText: S.of(context).cancel,
actionLeftButton: () =>
Navigator.pop(context, true),
actionRightButton: () =>
Navigator.pop(context, false)
);
});
},
onDismissed: (direction) async =>
await nodeList.remove(node: node),
direction: DismissDirection.endToStart,
background: Container(
padding: EdgeInsets.only(right: 10.0, top: 2),
alignment: AlignmentDirectional.centerEnd,
color: Palette.red,
child: Column(
children: <Widget>[
trashImage,
Text(
S.of(context).delete,
style: TextStyle(color: Colors.white),
)
],
)),
child: content);
},
);
})
),
)
)
],
),
);
@@ -9,8 +9,8 @@ class NodeIndicator extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 10.0,
height: 10.0,
width: 8.0,
height: 8.0,
decoration: BoxDecoration(shape: BoxShape.circle, color: color),
);
}
@@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
class NodeListRow extends StatelessWidget {
NodeListRow({
@required this.title,
@required this.trailing,
@required this.color,
@required this.textColor,
@required this.onTap,
@required this.isDrawTop,
@required this.isDrawBottom});
final String title;
final Widget trailing;
final Color color;
final Color textColor;
final VoidCallback onTap;
final bool isDrawTop;
final bool isDrawBottom;
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
isDrawTop
? Container(
width: double.infinity,
height: 1,
color: Theme.of(context).dividerColor,
)
: Offstage(),
Container(
width: double.infinity,
height: 56,
color: color,
child: ListTile(
contentPadding: EdgeInsets.only(
left: 24,
right: 24,
),
title: Text(
title,
style: TextStyle(
fontSize: 14,
color: textColor
),
textAlign: TextAlign.left),
trailing: trailing,
onTap: onTap,
)
),
isDrawBottom
? Container(
width: double.infinity,
height: 1,
color: Theme.of(context).dividerColor,
)
: Offstage(),
],
);
}
}
+28 -16
View File
@@ -1,7 +1,6 @@
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/generated/i18n.dart';
@@ -29,8 +28,6 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
static const defaultPinLength = 4;
static const sixPinLength = 6;
static const fourPinLength = 4;
static final deleteIconImage = Image.asset('assets/images/delete_icon.png');
static final backArrowImage = Image.asset('assets/images/back_arrow.png');
final _gridViewKey = GlobalKey();
int pinLength = defaultPinLength;
@@ -89,6 +86,15 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
Widget build(BuildContext context) => Scaffold(body: body(context));
Widget body(BuildContext context) {
final deleteIconImage = Image.asset(
'assets/images/delete_icon.png',
color: Theme.of(context).primaryTextTheme.title.color,
);
final faceImage = Image.asset(
'assets/images/face.png',
color: Theme.of(context).primaryTextTheme.title.color,
);
return SafeArea(
child: Container(
color: Theme.of(context).backgroundColor,
@@ -96,7 +102,11 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
child: Column(children: <Widget>[
Spacer(flex: 2),
Text(title,
style: TextStyle(fontSize: 24, color: Palette.wildDarkBlue)),
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color
)),
Spacer(flex: 3),
Container(
width: 180,
@@ -111,8 +121,9 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isFilled ? Palette.deepPurple : Colors.transparent,
border: Border.all(color: Palette.wildDarkBlue),
color: isFilled
? Theme.of(context).primaryTextTheme.title.color
: Theme.of(context).primaryTextTheme.caption.color,
));
}),
),
@@ -127,7 +138,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
},
child: Text(
_changePinLengthText(),
style: TextStyle(fontSize: 16.0, color: Palette.wildDarkBlue),
style: TextStyle(fontSize: 14.0, color: Theme.of(context).primaryTextTheme.caption.color),
))
],
Spacer(flex: 1),
@@ -149,9 +160,11 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
return Container(
margin: EdgeInsets.only(
left: marginLeft, right: marginRight),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).buttonColor,
child: FlatButton(
onPressed: () {},
color: Theme.of(context).backgroundColor,
shape: CircleBorder(),
child: faceImage,
),
);
} else if (index == 10) {
@@ -162,7 +175,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
left: marginLeft, right: marginRight),
child: FlatButton(
onPressed: () => _pop(),
color: Theme.of(context).buttonColor,
color: Theme.of(context).backgroundColor,
shape: CircleBorder(),
child: deleteIconImage,
),
@@ -176,14 +189,13 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
left: marginLeft, right: marginRight),
child: FlatButton(
onPressed: () => _push(index),
color: Theme.of(context)
.accentTextTheme
.title
.backgroundColor,
color: Theme.of(context).backgroundColor,
shape: CircleBorder(),
child: Text('$index',
style: TextStyle(
fontSize: 23.0, color: Palette.blueGrey)),
fontSize: 30.0,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color)),
),
);
}),
+314 -240
View File
@@ -3,58 +3,31 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:esys_flutter_share/esys_flutter_share.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/stores/subaddress_list/subaddress_list_store.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/screens/receive/qr_image.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
import 'package:cake_wallet/src/screens/accounts/account_list_page.dart';
import 'package:cake_wallet/src/stores/account_list/account_list_store.dart';
import 'package:cake_wallet/src/screens/receive/widgets/header_tile.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/themes.dart';
import 'package:cake_wallet/theme_changer.dart';
class ReceivePage extends BasePage {
class ReceivePage extends StatefulWidget {
@override
bool get isModalBackButton => true;
@override
String get title => S.current.receive;
@override
Widget trailing(BuildContext context) {
final walletStore = Provider.of<WalletStore>(context);
return SizedBox(
height: 37.0,
width: 37.0,
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: () => Share.text(
'Share address', walletStore.getAddress, 'text/plain'),
child: Icon(
Icons.share,
size: 30.0,
)),
),
);
}
@override
Widget body(BuildContext context) =>
SingleChildScrollView(child: ReceiveBody());
ReceivePageState createState() => ReceivePageState();
}
class ReceiveBody extends StatefulWidget {
@override
ReceiveBodyState createState() => ReceiveBodyState();
}
class ReceiveBodyState extends State<ReceiveBody> {
class ReceivePageState extends State<ReceivePage> {
final amountController = TextEditingController();
final _formKey = GlobalKey<FormState>();
final _backArrowImage = Image.asset('assets/images/back_arrow.png');
final _backArrowImageDarkTheme =
Image.asset('assets/images/back_arrow_dark_theme.png');
@override
void dispose() {
@@ -65,14 +38,30 @@ class ReceiveBodyState extends State<ReceiveBody> {
@override
Widget build(BuildContext context) {
final walletStore = Provider.of<WalletStore>(context);
SubaddressListStore subaddressListStore;
final subaddressListStore = Provider.of<SubaddressListStore>(context);
final accountListStore = Provider.of<AccountListStore>(context);
try {
subaddressListStore = Provider.of<SubaddressListStore>(context);
} catch (_) {}
final shareImage = Image.asset('assets/images/share.png',
color: Theme.of(context).primaryTextTheme.title.color,
);
final copyImage = Image.asset('assets/images/copy_content.png',
color: Theme.of(context).primaryTextTheme.title.color,
);
final currentColor = Theme.of(context).selectedRowColor;
final notCurrentColor = Theme.of(context).scaffoldBackgroundColor;
final currentColor = Theme.of(context).accentTextTheme.subtitle.decorationColor;
final notCurrentColor = Theme.of(context).backgroundColor;
final currentTextColor = Colors.blue;
final notCurrentTextColor = Theme.of(context).primaryTextTheme.caption.color;
final _themeChanger = Provider.of<ThemeChanger>(context);
Image _backButton;
if (_themeChanger.getTheme() == Themes.darkTheme) {
_backButton = _backArrowImageDarkTheme;
} else {
_backButton = _backArrowImage;
}
amountController.addListener(() {
if (_formKey.currentState.validate()) {
@@ -82,207 +71,292 @@ class ReceiveBodyState extends State<ReceiveBody> {
}
});
return SafeArea(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.all(35.0),
color: Theme.of(context).backgroundColor,
return Scaffold(
resizeToAvoidBottomPadding: false,
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.only(top: 24),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).scaffoldBackgroundColor,
Theme.of(context).primaryColor
],
begin: Alignment.centerLeft,
end: Alignment.centerRight
)
),
child: Column(
children: <Widget>[
Observer(builder: (_) {
return Row(
Container(
padding: EdgeInsets.only(
top: 10,
bottom: 20,
left: 5,
right: 10
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Spacer(
flex: 1,
SizedBox(
height: 44,
width: 44,
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: () => Navigator.of(context).pop(),
child: _backButton),
),
),
Flexible(
flex: 2,
child: AspectRatio(
aspectRatio: 1.0,
child: Container(
padding: EdgeInsets.all(5),
color: Colors.white,
child: QrImage(
data: walletStore.getAddress +
walletStore.amountValue,
backgroundColor: Colors.transparent,
),
),
)),
Spacer(
flex: 1,
Text(
S.of(context).receive,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color),
),
SizedBox(
height: 44.0,
width: 44.0,
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: () => Share.text(
S.current.share_address, walletStore.subaddress.address, 'text/plain'),
child: shareImage),
),
)
],
);
}),
Observer(builder: (_) {
return Row(
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.all(20.0),
child: Center(
child: GestureDetector(
onTap: () {
Clipboard.setData(
ClipboardData(text: walletStore.getAddress));
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
S.of(context).copied_to_clipboard,
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.green,
));
},
child: Text(
walletStore.getAddress,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
),
),
),
Expanded(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Observer(builder: (_) {
return Row(
children: <Widget>[
Spacer(
flex: 1,
),
Flexible(
flex: 2,
child: Center(
child: AspectRatio(
aspectRatio: 1.0,
child: QrImage(
data: walletStore.subaddress.address +
walletStore.amountValue,
backgroundColor: Colors.transparent,
foregroundColor: Theme.of(context).primaryTextTheme.display4.color,
),
),
)),
Spacer(
flex: 1,
)
],
);
}),
Padding(
padding: EdgeInsets.all(24),
child: Row(
children: <Widget>[
Expanded(
child: Form(
key: _formKey,
child: BaseTextFormField(
controller: amountController,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [
BlacklistingTextInputFormatter(
RegExp('[\\-|\\ |\\,]'))
],
textAlign: TextAlign.center,
hintText: S.of(context).receive_amount,
borderColor: Theme.of(context).primaryTextTheme.caption.color,
validator: (value) {
walletStore.validateAmount(value);
return walletStore.errorMessage;
},
autovalidate: true,
)
)
)
],
),
),
))
],
);
}),
Row(
children: <Widget>[
Expanded(
child: Form(
key: _formKey,
child: TextFormField(
keyboardType:
TextInputType.numberWithOptions(decimal: true),
inputFormatters: [
BlacklistingTextInputFormatter(
RegExp('[\\-|\\ |\\,]'))
],
style: TextStyle(
fontSize: 14.0,
),
decoration: InputDecoration(
hintStyle: TextStyle(
color: Theme.of(context).hintColor),
hintText: S.of(context).amount,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
width: 1.0))),
validator: (value) {
walletStore.validateAmount(value);
return walletStore.errorMessage;
},
autovalidate: true,
controller: amountController,
)))
],
)
],
),
),
subaddressListStore != null
? Row(
children: <Widget>[
Expanded(
child: Container(
color: Theme.of(context).accentTextTheme.headline.color,
child: Column(
children: <Widget>[
ListTile(
title: Text(
S.of(context).subaddresses,
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context)
.primaryTextTheme
.headline
.color),
),
trailing: Container(
width: 28.0,
height: 28.0,
decoration: BoxDecoration(
color: Theme.of(context).selectedRowColor,
shape: BoxShape.circle),
child: InkWell(
onTap: () => Navigator.of(context)
.pushNamed(Routes.newSubaddress),
borderRadius:
BorderRadius.all(Radius.circular(14.0)),
child: Icon(
Icons.add,
color: Palette.violet,
size: 22.0,
),
),
),
),
Divider(
color: Theme.of(context).dividerTheme.color,
height: 1.0,
)
],
),
))
],
)
: Container(),
subaddressListStore != null
? Observer(builder: (_) {
return ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: subaddressListStore.subaddresses.length,
separatorBuilder: (context, i) {
return Divider(
color: Theme.of(context).dividerTheme.color,
height: 1.0,
);
},
itemBuilder: (context, i) {
return Observer(builder: (_) {
final subaddress = subaddressListStore.subaddresses[i];
final isCurrent =
walletStore.getAddress == subaddress.address;
final label = subaddress.label.isNotEmpty
? subaddress.label
: subaddress.address;
return InkWell(
onTap: () => walletStore.setSubaddress(subaddress),
child: Container(
color: isCurrent ? currentColor : notCurrentColor,
child: Column(children: <Widget>[
ListTile(
title: Text(
label,
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context)
.primaryTextTheme
.headline
.color),
Padding(
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
child: Builder(
builder: (context) => Observer(
builder: (context) => GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(
text: walletStore.subaddress.address));
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
S.of(context).copied_to_clipboard,
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 500),
));
},
child: Container(
height: 48,
padding: EdgeInsets.only(left: 24, right: 24),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(24)),
color: Theme.of(context).primaryTextTheme.overline.color
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Text(
walletStore.subaddress.address,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color
),
),
),
Padding(
padding: EdgeInsets.only(left: 12),
child: copyImage,
)
],
),
),
)
]),
),
);
});
});
})
: Container()
],
)));
)
),
),
Observer(
builder: (_) => ListView.separated(
separatorBuilder: (context, index) => Divider(
height: 1,
color: Theme.of(context).dividerColor,
),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: subaddressListStore.subaddresses.length + 2,
itemBuilder: (context, index) {
if (index == 0) {
return ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24)
),
child: HeaderTile(
onTap: () async {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AccountListPage(accountListStore: accountListStore);
}
);
},
title: walletStore.account.label,
icon: Icon(
Icons.arrow_forward_ios,
size: 14,
color: Theme.of(context).primaryTextTheme.title.color,
)
),
);
}
if (index == 1) {
return HeaderTile(
onTap: () => Navigator.of(context)
.pushNamed(Routes.newSubaddress),
title: S.of(context).subaddresses,
icon: Icon(
Icons.add,
size: 20,
color: Theme.of(context).primaryTextTheme.title.color,
)
);
}
index -= 2;
return Observer(
builder: (_) {
final subaddress = subaddressListStore.subaddresses[index];
final isCurrent =
walletStore.subaddress.address == subaddress.address;
final label = subaddress.label.isNotEmpty
? subaddress.label
: subaddress.address;
final content = InkWell(
onTap: () => walletStore.setSubaddress(subaddress),
child: Container(
color: isCurrent ? currentColor : notCurrentColor,
padding: EdgeInsets.only(
left: 24,
right: 24,
top: 28,
bottom: 28
),
child: Text(
label,
style: TextStyle(
fontSize: subaddress.label.isNotEmpty
? 18 : 10,
fontWeight: FontWeight.bold,
color: isCurrent
? currentTextColor
: notCurrentTextColor,
),
),
),
);
return isCurrent
? content
: Slidable(
key: Key(subaddress.address),
actionPane: SlidableDrawerActionPane(),
child: content,
secondaryActions: <Widget>[
IconSlideAction(
caption: S.of(context).edit,
color: Theme.of(context).primaryTextTheme.overline.color,
icon: Icons.edit,
onTap: () => Navigator.of(context)
.pushNamed(Routes.newSubaddress, arguments: subaddress),
)
]
);
}
);
}
)
),
],
),
)
)
],
)
),
);
}
}
}
@@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
class HeaderTile extends StatelessWidget {
HeaderTile({
@required this.onTap,
@required this.title,
@required this.icon
});
final VoidCallback onTap;
final String title;
final Icon icon;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.only(
left: 24,
right: 24,
top: 24,
bottom: 24
),
color: Theme.of(context).backgroundColor,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color
),
),
Container(
height: 32,
width: 32,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).primaryTextTheme.overline.color
),
child: icon,
)
],
),
),
);
}
}
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:qr/qr.dart';
import 'package:cake_wallet/src/screens/receive/qr_painter.dart';
import 'package:cake_wallet/src/screens/receive/widgets/qr_painter.dart';
class QrImage extends StatelessWidget {
QrImage({
+3 -5
View File
@@ -17,7 +17,7 @@ class RescanPage extends BasePage {
final rescanWalletStore = Provider.of<RescanWalletStore>(context);
return Padding(
padding: EdgeInsets.only(left: 20, right: 20, bottom: 20),
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
child:
Column(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
BlockchainHeightWidget(key: blockchainKey),
@@ -31,10 +31,8 @@ class RescanPage extends BasePage {
restoreHeight: blockchainKey.currentState.height);
Navigator.of(context).pop();
},
color:
Theme.of(context).primaryTextTheme.button.backgroundColor,
borderColor:
Theme.of(context).primaryTextTheme.button.decorationColor))
color: Colors.blue,
textColor: Colors.white,))
]),
);
}
@@ -4,8 +4,6 @@ import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/routes.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/src/screens/restore/widgets/restore_button.dart';
import 'package:cake_wallet/src/screens/restore/widgets/image_widget.dart';
import 'package:cake_wallet/src/screens/restore/widgets/base_restore_widget.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart';
@@ -18,46 +16,38 @@ class RestoreOptionsPage extends BasePage {
@override
String get title => S.current.restore_restore_wallet;
@override
Color get backgroundColor => Palette.creamyGrey;
final _imageSeedKeys = Image.asset('assets/images/seedKeys.png');
final _imageRestoreSeed = Image.asset('assets/images/restoreSeed.png');
final imageSeedKeys = Image.asset('assets/images/restore_wallet_image.png');
final imageBackup = Image.asset('assets/images/backup.png');
@override
Widget body(BuildContext context) {
final isLargeScreen = MediaQuery.of(context).size.height > largeHeight;
return BaseRestoreWidget(
firstRestoreButton: RestoreButton(
onPressed: () =>
Navigator.pushNamed(
context, Routes.restoreWalletOptionsFromWelcome),
imageWidget: ImageWidget(
image: _imageSeedKeys,
aspectRatioImage: _aspectRatioImage,
isLargeScreen: isLargeScreen,
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(
onPressed: () {},
image: imageBackup,
title: S.of(context).restore_title_from_backup,
description: S.of(context).restore_description_from_backup
),
)
],
),
titleColor: Palette.lightViolet,
color: Palette.lightViolet,
title: S.of(context).restore_title_from_seed_keys,
description: S.of(context).restore_description_from_seed_keys,
textButton: S.of(context).restore_next,
),
secondRestoreButton: RestoreButton(
onPressed: () {},
imageWidget: ImageWidget(
image: _imageRestoreSeed,
aspectRatioImage: _aspectRatioImage,
isLargeScreen: isLargeScreen,
),
titleColor: Palette.cakeGreen,
color: Palette.cakeGreen,
title: S.of(context).restore_title_from_backup,
description: S.of(context).restore_description_from_backup,
textButton: S.of(context).restore_next,
),
isLargeScreen: isLargeScreen,
)
);
}
}
@@ -14,8 +14,8 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/stores/seed_language/seed_language_store.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
class RestoreWalletFromKeysPage extends BasePage {
RestoreWalletFromKeysPage(
@@ -47,11 +47,36 @@ class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
final _viewKeyController = TextEditingController();
final _spendKeyController = TextEditingController();
@override
void dispose() {
_nameController.dispose();
_addressController.dispose();
_viewKeyController.dispose();
_spendKeyController.dispose();
super.dispose();
}
void onHandleControllers(WalletRestorationStore walletRestorationStore) {
if (_nameController.text.isNotEmpty &&
_addressController.text.isNotEmpty &&
_viewKeyController.text.isNotEmpty &&
_spendKeyController.text.isNotEmpty) {
walletRestorationStore.setDisabledState(false);
} else {
walletRestorationStore.setDisabledState(true);
}
}
@override
Widget build(BuildContext context) {
final walletRestorationStore = Provider.of<WalletRestorationStore>(context);
final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
_nameController.addListener(() {onHandleControllers(walletRestorationStore);});
_addressController.addListener(() {onHandleControllers(walletRestorationStore);});
_viewKeyController.addListener(() {onHandleControllers(walletRestorationStore);});
_spendKeyController.addListener(() {onHandleControllers(walletRestorationStore);});
reaction((_) => walletRestorationStore.state, (WalletRestorationState state) {
if (state is WalletRestoredSuccessfully) {
Navigator.of(context).popUntil((route) => route.isFirst);
@@ -62,48 +87,48 @@ class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text(state.error),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () => Navigator.of(context).pop(),
),
],
return AlertWithOneAction(
alertTitle: S.current.restore_title_from_keys,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
});
}
});
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 20.0, right: 20.0, bottom: 20.0),
content: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 13, right: 13),
child: Column(children: <Widget>[
Row(
children: <Widget>[
Flexible(
child: Container(
return Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24.0),
content: Form(
key: _formKey,
child: Column(children: <Widget>[
Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(top: 20.0),
child: TextFormField(
style: TextStyle(fontSize: 14.0),
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
controller: _nameController,
decoration: InputDecoration(
hintStyle:
TextStyle(color: Theme.of(context).hintColor),
hintStyle: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: S.of(context).restore_wallet_name,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
color: Theme.of(context).dividerColor,
width: 1.0))),
validator: (value) {
walletRestorationStore.validateWalletName(value);
@@ -111,28 +136,34 @@ class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
},
),
))
],
),
Row(
children: <Widget>[
Flexible(
child: Container(
],
),
Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(top: 20.0),
child: TextFormField(
style: TextStyle(fontSize: 14.0),
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
controller: _addressController,
keyboardType: TextInputType.multiline,
maxLines: null,
decoration: InputDecoration(
hintStyle:
TextStyle(color: Theme.of(context).hintColor),
hintStyle: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: S.of(context).restore_address,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
color: Theme.of(context).dividerColor,
width: 1.0))),
validator: (value) {
walletRestorationStore.validateAddress(value);
@@ -140,26 +171,32 @@ class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
},
),
))
],
),
Row(
children: <Widget>[
Flexible(
child: Container(
],
),
Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(top: 20.0),
child: TextFormField(
style: TextStyle(fontSize: 14.0),
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
controller: _viewKeyController,
decoration: InputDecoration(
hintStyle:
TextStyle(color: Theme.of(context).hintColor),
hintStyle: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: S.of(context).restore_view_key_private,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
color: Theme.of(context).dividerColor,
width: 1.0))),
validator: (value) {
walletRestorationStore.validateKeys(value);
@@ -167,26 +204,32 @@ class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
},
),
))
],
),
Row(
children: <Widget>[
Flexible(
child: Container(
],
),
Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(top: 20.0),
child: TextFormField(
style: TextStyle(fontSize: 14.0),
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
controller: _spendKeyController,
decoration: InputDecoration(
hintStyle:
TextStyle(color: Theme.of(context).hintColor),
hintStyle: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: S.of(context).restore_spend_key_private,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
color: Theme.of(context).dividerColor,
width: 1.0))),
validator: (value) {
walletRestorationStore.validateKeys(value);
@@ -194,16 +237,14 @@ class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
},
),
))
],
),
BlockchainHeightWidget(key: _blockchainHeightKey),
]),
)
],
],
),
BlockchainHeightWidget(key: _blockchainHeightKey),
]),
),
),
bottomSection: Observer(builder: (_) {
return LoadingPrimaryButton(
bottomSectionPadding: EdgeInsets.only(bottom: 24),
bottomSection: Observer(builder: (_) {
return LoadingPrimaryButton(
onPressed: () {
if (_formKey.currentState.validate()) {
walletRestorationStore.restoreFromKeys(
@@ -216,10 +257,12 @@ class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
}
},
text: S.of(context).restore_recover,
color: Theme.of(context).primaryTextTheme.button.backgroundColor,
borderColor:
Theme.of(context).primaryTextTheme.button.decorationColor);
}),
color: Colors.green,
textColor: Colors.white,
isDisabled: walletRestorationStore.disabledState,
);
}),
),
);
}
}
@@ -10,7 +10,7 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
class RestoreWalletFromSeedDetailsPage extends BasePage {
@override
@@ -32,10 +32,24 @@ class _RestoreFromSeedDetailsFormState
final _blockchainHeightKey = GlobalKey<BlockchainHeightState>();
final _nameController = TextEditingController();
@override
void dispose() {
_nameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final walletRestorationStore = Provider.of<WalletRestorationStore>(context);
_nameController.addListener(() {
if (_nameController.text.isNotEmpty) {
walletRestorationStore.setDisabledState(false);
} else {
walletRestorationStore.setDisabledState(true);
}
});
reaction((_) => walletRestorationStore.state, (WalletRestorationState state) {
if (state is WalletRestoredSuccessfully) {
Navigator.of(context).popUntil((route) => route.isFirst);
@@ -46,68 +60,65 @@ class _RestoreFromSeedDetailsFormState
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text(state.error),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () => Navigator.of(context).pop(),
),
],
return AlertWithOneAction(
alertTitle: S.current.restore_title_from_seed,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
});
}
});
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 20.0, right: 20.0, bottom: 20.0),
content: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 13, right: 13),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(top: 20.0),
child: TextFormField(
style: TextStyle(fontSize: 14.0),
controller: _nameController,
decoration: InputDecoration(
hintStyle: TextStyle(
color: Theme.of(context).hintColor),
hintText: S.of(context).restore_wallet_name,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen,
width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
width: 1.0))),
validator: (value) {
walletRestorationStore
.validateWalletName(value);
return walletRestorationStore.errorMessage;
},
return Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24.0),
content: Form(
key: _formKey,
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(top: 20.0),
child: TextFormField(
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
))
],
),
BlockchainHeightWidget(key: _blockchainHeightKey),
]))
],
controller: _nameController,
decoration: InputDecoration(
hintStyle: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: S.of(context).restore_wallet_name,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
validator: (value) {
walletRestorationStore
.validateWalletName(value);
return walletRestorationStore.errorMessage;
},
),
))
],
),
BlockchainHeightWidget(key: _blockchainHeightKey),
]),
),
),
bottomSection: Observer(builder: (_) {
return LoadingPrimaryButton(
bottomSectionPadding: EdgeInsets.only(bottom: 24),
bottomSection: Observer(builder: (_) {
return LoadingPrimaryButton(
onPressed: () {
if (_formKey.currentState.validate()) {
walletRestorationStore.restoreFromSeed(
@@ -117,10 +128,12 @@ class _RestoreFromSeedDetailsFormState
},
isLoading: walletRestorationStore.state is WalletIsRestoring,
text: S.of(context).restore_recover,
color: Theme.of(context).primaryTextTheme.button.backgroundColor,
borderColor:
Theme.of(context).primaryTextTheme.button.decorationColor);
}),
color: Colors.green,
textColor: Colors.white,
isDisabled: walletRestorationStore.disabledState,
);
}),
),
);
}
}
@@ -11,6 +11,7 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/stores/wallet_restoration/wallet_restoration_store.dart';
import 'package:cake_wallet/src/widgets/seed_widget.dart';
import 'package:cake_wallet/src/stores/seed_language/seed_language_store.dart';
import 'package:cake_wallet/palette.dart';
class RestoreWalletFromSeedPage extends BasePage {
RestoreWalletFromSeedPage(
@@ -27,13 +28,10 @@ class RestoreWalletFromSeedPage extends BasePage {
String get title => S.current.restore_title_from_seed;
@override
Widget trailing(BuildContext context) => SizedBox(
width: 80,
height: 20,
child: FlatButton(
child: Text(S.of(context).clear),
padding: EdgeInsets.all(0),
onPressed: () => formKey?.currentState?.clear()));
Color get backgroundLightColor => Palette.lavender;
@override
Color get backgroundDarkColor => PaletteDark.lightNightBlue;
@override
Widget body(BuildContext context) => RestoreFromSeedForm(key: formKey);
@@ -59,7 +57,7 @@ class _RestoreFromSeedFormState extends State<RestoreFromSeedForm> {
onTap: () =>
SystemChannels.textInput.invokeMethod<void>('TextInput.hide'),
child: Container(
padding: EdgeInsets.only(left: 20.0, right: 20.0),
color: Theme.of(context).backgroundColor,
child: SeedWidget(
key: _seedKey,
onMnemoticChange: (seed) => walletRestorationStore.setSeed(seed),
@@ -3,8 +3,6 @@ import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/restore/widgets/restore_button.dart';
import 'package:cake_wallet/src/screens/restore/widgets/image_widget.dart';
import 'package:cake_wallet/src/screens/restore/widgets/base_restore_widget.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:cake_wallet/generated/i18n.dart';
@@ -20,49 +18,43 @@ class RestoreWalletOptionsPage extends BasePage {
@override
String get title => S.current.restore_seed_keys_restore;
@override
Color get backgroundColor => Palette.creamyGrey;
final _imageSeed = Image.asset('assets/images/seedIco.png');
final _imageKeys = Image.asset('assets/images/keysIco.png');
final imageSeed = Image.asset('assets/images/restore_seed.png');
final imageKeys = Image.asset('assets/images/restore_keys.png');
@override
Widget body(BuildContext context) {
final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
final isLargeScreen = MediaQuery.of(context).size.height > largeHeight;
return BaseRestoreWidget(
firstRestoreButton: RestoreButton(
onPressed: () {
seedLanguageStore.setCurrentRoute(Routes.restoreWalletFromSeed);
Navigator.pushNamed(context, Routes.seedLanguage, arguments: type);
},
imageWidget: ImageWidget(
image: _imageSeed,
aspectRatioImage: _aspectRatioImage,
isLargeScreen: isLargeScreen),
titleColor: Palette.lightViolet,
color: Palette.lightViolet,
title: S.of(context).restore_title_from_seed,
description: S.of(context).restore_description_from_seed,
textButton: S.of(context).restore_next,
),
secondRestoreButton: RestoreButton(
onPressed: () {
seedLanguageStore.setCurrentRoute(Routes.restoreWalletFromKeys);
Navigator.pushNamed(context, Routes.seedLanguage, arguments: type);
},
imageWidget: ImageWidget(
image: _imageKeys,
aspectRatioImage: _aspectRatioImage,
isLargeScreen: isLargeScreen),
titleColor: Palette.cakeGreen,
color: Palette.cakeGreen,
title: S.of(context).restore_title_from_keys,
description: S.of(context).restore_description_from_keys,
textButton: S.of(context).restore_next,
),
isLargeScreen: isLargeScreen,
);
return Container(
width: double.infinity,
height: double.infinity,
padding: EdgeInsets.all(24),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
RestoreButton(
onPressed: () {
seedLanguageStore
.setCurrentRoute(Routes.restoreWalletFromSeed);
Navigator.pushNamed(context, Routes.seedLanguage);
},
image: imageSeed,
title: S.of(context).restore_title_from_seed,
description: S.of(context).restore_description_from_seed),
Padding(
padding: EdgeInsets.only(top: 24),
child: RestoreButton(
onPressed: () {
seedLanguageStore
.setCurrentRoute(Routes.restoreWalletFromKeys);
Navigator.pushNamed(context, Routes.seedLanguage);
},
image: imageKeys,
title: S.of(context).restore_title_from_keys,
description: S.of(context).restore_description_from_keys),
)
],
),
));
}
}
@@ -1,44 +0,0 @@
import 'package:flutter/material.dart';
const largeHeight = 700;
class BaseRestoreWidget extends StatelessWidget {
BaseRestoreWidget({
@required this.firstRestoreButton,
@required this.secondRestoreButton,
this.isLargeScreen = false
});
final Widget firstRestoreButton;
final Widget secondRestoreButton;
final bool isLargeScreen;
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(
left: 20.0,
right: 20.0,
),
child: isLargeScreen
? Column(
children: <Widget>[
Flexible(
child: firstRestoreButton
),
Flexible(
child: secondRestoreButton
)
],
)
: SingleChildScrollView(
child: Column(
children: <Widget>[
firstRestoreButton,
secondRestoreButton
],
),
)
);
}
}
@@ -1,29 +0,0 @@
import 'package:flutter/material.dart';
class ImageWidget extends StatelessWidget {
ImageWidget({
@required this.image,
@required this.aspectRatioImage,
this.isLargeScreen = false});
final Image image;
final double aspectRatioImage;
final bool isLargeScreen;
@override
Widget build(BuildContext context) {
return isLargeScreen
? Flexible(
child: Container(
child: AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(
fit: BoxFit.contain,
child: image,
),
),
),
)
: image;
}
}
@@ -1,101 +1,70 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:auto_size_text/auto_size_text.dart';
class RestoreButton extends StatelessWidget {
const RestoreButton(
{@required this.onPressed,
@required this.imageWidget,
@required this.color,
@required this.titleColor,
this.title = '',
this.description = '',
this.textButton = ''});
const RestoreButton({
@required this.onPressed,
@required this.image,
@required this.title,
@required this.description});
final VoidCallback onPressed;
final Widget imageWidget;
final Color color;
final Color titleColor;
final Image image;
final String title;
final String description;
final String textButton;
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 20.0, bottom: 20.0),
decoration: BoxDecoration(
color: Theme.of(context).accentTextTheme.headline.backgroundColor,
borderRadius: BorderRadius.all(Radius.circular(20.0)),
boxShadow: [
BoxShadow(
color: Palette.buttonShadow,
blurRadius: 10,
offset: Offset(
0,
12,
return GestureDetector(
onTap: onPressed,
child: Container(
width: double.infinity,
height: 150,
padding: EdgeInsets.all(24),
alignment: Alignment.topLeft,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12)),
color: Theme.of(context).accentTextTheme.title.backgroundColor,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
image,
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 16),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
description,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryTextTheme.caption.color
),
),
)
],
),
),
)
]),
child: InkWell(
onTap: onPressed,
borderRadius: BorderRadius.all(Radius.circular(20.0)),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
imageWidget,
Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 20, right: 20),
child: AutoSizeText(
title,
textAlign: TextAlign.center,
style: TextStyle(
color: titleColor,
fontWeight: FontWeight.bold),
maxLines: 2,
),
),
Padding(
padding: EdgeInsets.only(left: 20, right: 20, top: 10),
child: AutoSizeText(
description,
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).accentTextTheme.subhead.color,
),
maxLines: 2,
)
)
],
),
SizedBox(
height: 20,
),
Container(
height: 56.0,
decoration: BoxDecoration(
border: Border(
top: BorderSide(
color: Theme.of(context)
.accentTextTheme
.headline
.decorationColor,
width: 1.15)),
color: Colors.transparent,
),
child: Center(
child: Text(
textButton,
style: TextStyle(
color: color,
fontSize: 16.0,
fontWeight: FontWeight.bold),
),
))
],
)),
],
),
),
);
}
}
+120 -126
View File
@@ -4,19 +4,16 @@ import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/stores/wallet_seed/wallet_seed_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
class SeedPage extends BasePage {
SeedPage({this.onCloseCallback});
static final image = Image.asset('assets/images/seed_image.png');
@override
bool get isModalBackButton => true;
static final image = Image.asset('assets/images/crypto_lock.png');
@override
String get title => S.current.seed_title;
@@ -32,135 +29,132 @@ class SeedPage extends BasePage {
return onCloseCallback != null ? Offstage() : super.leading(context);
}
@override
Widget trailing(BuildContext context) {
return onCloseCallback != null
? GestureDetector(
onTap: () => onClose(context),
child: Container(
width: 70,
height: 32,
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(16)),
color: Theme.of(context).accentTextTheme.title.color
),
child: Text(
S.of(context).seed_language_next,
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w600,
color: Colors.blue
),
),
),
)
: Offstage();
}
@override
Widget body(BuildContext context) {
final walletSeedStore = Provider.of<WalletSeedStore>(context);
String _seed;
return Container(
padding: EdgeInsets.all(30.0),
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
image,
Container(
margin: EdgeInsets.only(left: 30.0, top: 10.0, right: 30.0),
child: Observer(builder: (_) {
_seed = walletSeedStore.seed;
return Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color: Theme.of(context)
.dividerColor))),
padding: EdgeInsets.only(bottom: 20.0),
margin: EdgeInsets.only(bottom: 10.0),
child: Text(
walletSeedStore.name,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18.0,
color: Theme.of(context)
.primaryTextTheme
.button
.color),
),
))
],
),
SizedBox(
height: 10.0,
),
Text(
walletSeedStore.seed,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
)
],
);
}),
),
Container(
margin: EdgeInsets.only(top: 30.0),
child: Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(right: 8.0),
child: PrimaryButton(
onPressed: () => Share.text(
S.of(context).seed_share,
_seed,
'text/plain'),
color: Theme.of(context)
.primaryTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.primaryTextTheme
.button
.decorationColor,
text: S.of(context).save),
)),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 8.0),
child: Builder(
builder: (context) => PrimaryButton(
onPressed: () {
Clipboard.setData(
ClipboardData(text: _seed));
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(S
.of(context)
.copied_to_clipboard),
backgroundColor: Colors.green,
duration:
Duration(milliseconds: 1500),
),
);
},
text: S.of(context).copy,
color: Theme.of(context)
.accentTextTheme
.caption
.backgroundColor,
borderColor: Theme.of(context)
.accentTextTheme
.caption
.decorationColor),
)))
],
),
)
],
),
width: double.infinity,
height: double.infinity,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 40, right: 40, bottom: 20),
content: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 33),
child: image,
),
Padding(
padding: EdgeInsets.only(top: 33),
child: Observer(
builder: (_) {
_seed = walletSeedStore.seed;
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
walletSeedStore.name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color
),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: Text(
_seed,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryTextTheme.caption.color
),
),
)
],
);
}
),
)
],
),
bottomSectionPadding: EdgeInsets.only(
left: 24,
right: 24,
bottom: 52
),
bottomSection: Container(
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(right: 8.0),
child: PrimaryButton(
onPressed: () => Share.text(
S.of(context).seed_share,
_seed,
'text/plain'),
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: _seed));
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(S
.of(context)
.copied_to_clipboard),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 1500),
),
);
},
text: S.of(context).copy,
color: Colors.blue,
textColor: Colors.white)
),
)
)
],
),
onCloseCallback != null
? PrimaryButton(
onPressed: () => onClose(context),
text: S.of(context).restore_next,
color: Palette.darkGrey,
borderColor: Palette.darkGrey)
: Offstage()
],
),
),
);
}
@@ -1,52 +1,94 @@
import 'package:provider/provider.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/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
import 'package:provider/provider.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/src/stores/seed_language/seed_language_store.dart';
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
class SeedLanguage extends BasePage {
final imageSeed = Image.asset('assets/images/seedIco.png');
@override
Widget body(BuildContext context) => SeedLanguageForm();
}
class SeedLanguageForm extends StatefulWidget {
@override
SeedLanguageFormState createState() => SeedLanguageFormState();
}
class SeedLanguageFormState extends State<SeedLanguageForm> {
static const aspectRatioImage = 1.22;
final walletNameImage = Image.asset('assets/images/wallet_name.png');
@override
Widget body(BuildContext context) {
Widget build(BuildContext context) {
final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
final List<String> seedLocales = [
S.current.seed_language_english,
S.current.seed_language_chinese,
S.current.seed_language_dutch,
S.current.seed_language_german,
S.current.seed_language_japanese,
S.current.seed_language_portuguese,
S.current.seed_language_russian,
S.current.seed_language_spanish
];
return Container(
padding: EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
imageSeed,
Text(
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: walletNameImage, fit: BoxFit.fill)),
),
Padding(padding: EdgeInsets.only(top: 40),
child: Text(
S.of(context).seed_language_choose,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16.0),
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color
),
),
SizedBox(
height: 20.0,
),
Padding(padding: EdgeInsets.only(top: 24),
child: Observer(
builder: (_) => SelectButton(
image: null,
text: seedLocales[seedLanguages.indexOf(seedLanguageStore.selectedSeedLanguage)],
color: Theme.of(context).accentTextTheme.title.backgroundColor,
textColor: Theme.of(context).primaryTextTheme.title.color,
onTap: () async => await showDialog(
context: context,
builder: (BuildContext context) => SeedLanguagePicker()
)
)
),
SeedLanguagePicker(),
],
),
),
),
PrimaryButton(
onPressed: () =>
Navigator.of(context).popAndPushNamed(seedLanguageStore.currentRoute),
text: S.of(context).seed_language_next,
color:
Theme.of(context).primaryTextTheme.button.backgroundColor,
borderColor:
Theme.of(context).primaryTextTheme.button.decorationColor),
],
),
)
]),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Observer(
builder: (context) {
return PrimaryButton(
onPressed: () =>
Navigator.of(context).popAndPushNamed(seedLanguageStore.currentRoute),
text: S.of(context).seed_language_next,
color: Colors.green,
textColor: Colors.white);
},
)),
);
}
}
@@ -1,55 +1,238 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/src/stores/seed_language/seed_language_store.dart';
import 'package:cake_wallet/src/widgets/present_picker.dart';
import 'package:cake_wallet/generated/i18n.dart';
class SeedLanguagePicker extends StatelessWidget {
List<String> seedLocales = [
S.current.seed_language_english,
S.current.seed_language_chinese,
S.current.seed_language_dutch,
S.current.seed_language_german,
S.current.seed_language_japanese,
S.current.seed_language_portuguese,
S.current.seed_language_russian,
S.current.seed_language_spanish
];
List<Image> flagImages = [
Image.asset('assets/images/usa.png'),
Image.asset('assets/images/china.png'),
Image.asset('assets/images/holland.png'),
Image.asset('assets/images/germany.png'),
Image.asset('assets/images/japan.png'),
Image.asset('assets/images/portugal.png'),
Image.asset('assets/images/russia.png'),
Image.asset('assets/images/spain.png'),
];
List<String> languageCodes = [
'Eng',
'Chi',
'Ned',
'Ger',
'Jap',
'Por',
'Rus',
'Esp',
];
enum Places {topLeft, topRight, bottomLeft, bottomRight, inside}
class SeedLanguagePicker extends StatefulWidget {
@override
SeedLanguagePickerState createState() => SeedLanguagePickerState();
}
class SeedLanguagePickerState extends State<SeedLanguagePicker> {
@override
Widget build(BuildContext context) {
final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
return Observer(
builder: (_) => InkWell(
onTap: () => _setSeedLanguage(context),
return GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
color: Colors.transparent,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
child: Container(
padding: EdgeInsets.all(8.0),
//width: double.infinity,
decoration: BoxDecoration(
border: Border.all(
color: Theme.of(context).dividerTheme.color
),
borderRadius: BorderRadius.circular(8.0)
),
child: Text(seedLocales[seedLanguages.indexOf(seedLanguageStore.selectedSeedLanguage)],
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16.0, color: Palette.lightBlue),
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: Text(
S.of(context).seed_choose,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: Colors.white
),
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: GestureDetector(
onTap: () => null,
child: Container(
height: 300,
width: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(14)),
color: Theme.of(context).dividerColor
),
child: GridView.count(
shrinkWrap: true,
crossAxisCount: 3,
childAspectRatio: 1,
physics: const NeverScrollableScrollPhysics(),
crossAxisSpacing: 1,
mainAxisSpacing: 1,
children: List.generate(9, (index) {
if (index == 8) {
return gridTile(
isCurrent: false,
place: Places.bottomRight,
image: null,
text: '',
onTap: null);
} else {
final code = languageCodes[index];
final flag = flagImages[index];
final isCurrent = index == seedLanguages.indexOf(seedLanguageStore.selectedSeedLanguage);
if (index == 0) {
return gridTile(
isCurrent: isCurrent,
place: Places.topLeft,
image: flag,
text: code,
onTap: () {
seedLanguageStore.setSelectedSeedLanguage(seedLanguages[index]);
Navigator.of(context).pop();
}
);
}
if (index == 2) {
return gridTile(
isCurrent: isCurrent,
place: Places.topRight,
image: flag,
text: code,
onTap: () {
seedLanguageStore.setSelectedSeedLanguage(seedLanguages[index]);
Navigator.of(context).pop();
}
);
}
if (index == 6) {
return gridTile(
isCurrent: isCurrent,
place: Places.bottomLeft,
image: flag,
text: code,
onTap: () {
seedLanguageStore.setSelectedSeedLanguage(seedLanguages[index]);
Navigator.of(context).pop();
}
);
}
return gridTile(
isCurrent: isCurrent,
place: Places.inside,
image: flag,
text: code,
onTap: () {
seedLanguageStore.setSelectedSeedLanguage(seedLanguages[index]);
Navigator.of(context).pop();
}
);
}
}),
),
),
),
)
],
),
),
),
));
),
),
);
}
Future<void> _setSeedLanguage(BuildContext context) async {
final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
String selectedSeedLanguage =
await presentPicker(context, seedLocales);
Widget gridTile({
@required bool isCurrent,
@required Places place,
@required Image image,
@required String text,
@required VoidCallback onTap}) {
if (selectedSeedLanguage != null) {
selectedSeedLanguage = seedLanguages[seedLocales.indexOf(selectedSeedLanguage)];
seedLanguageStore.setSelectedSeedLanguage(selectedSeedLanguage);
BorderRadius borderRadius;
final color = isCurrent
? Theme.of(context).accentTextTheme.subtitle.decorationColor
: Theme.of(context).primaryTextTheme.display1.color;
final textColor = isCurrent
? Colors.blue
: Theme.of(context).primaryTextTheme.title.color;
switch (place) {
case Places.topLeft:
borderRadius = BorderRadius.only(topLeft: Radius.circular(14));
break;
case Places.topRight:
borderRadius = BorderRadius.only(topRight: Radius.circular(14));
break;
case Places.bottomLeft:
borderRadius = BorderRadius.only(bottomLeft: Radius.circular(14));
break;
case Places.bottomRight:
borderRadius = BorderRadius.only(bottomRight: Radius.circular(14));
break;
case Places.inside:
borderRadius = BorderRadius.all(Radius.circular(0));
break;
}
return GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: borderRadius,
color: color
),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
image != null
? image
: Offstage(),
Padding(
padding: image != null
? EdgeInsets.only(left: 10)
: EdgeInsets.only(left: 0),
child: Text(
text,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: textColor
),
),
)
],
),
),
)
);
}
}
+332 -310
View File
@@ -17,22 +17,58 @@ import 'package:cake_wallet/src/stores/send/send_store.dart';
import 'package:cake_wallet/src/stores/send/sending_state.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
import 'package:cake_wallet/src/domain/common/calculate_estimated_fee.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/sync_status.dart';
import 'package:cake_wallet/src/stores/sync/sync_store.dart';
import 'package:cake_wallet/src/widgets/top_panel.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
import 'package:cake_wallet/src/screens/send/widgets/sending_alert.dart';
import 'package:cake_wallet/src/widgets/template_tile.dart';
import 'package:cake_wallet/src/stores/send_template/send_template_store.dart';
class SendPage extends BasePage {
@override
String get title => S.current.send_title;
@override
bool get isModalBackButton => true;
Color get backgroundLightColor => Palette.lavender;
@override
Color get backgroundDarkColor => PaletteDark.lightNightBlue;
@override
bool get resizeToAvoidBottomPadding => false;
@override
Widget trailing(context) {
final sendStore = Provider.of<SendStore>(context);
return Container(
height: 32,
width: 82,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(16)),
color: Theme.of(context).accentTextTheme.title.color
),
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
onPressed: () => sendStore.clear(),
child: Text(
S.of(context).clear,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 10.0,
color: Colors.blue),
)),
),
);
}
@override
Widget body(BuildContext context) => SendForm();
}
@@ -44,7 +80,6 @@ class SendForm extends StatefulWidget {
class SendFormState extends State<SendForm> {
final _addressController = TextEditingController();
final _paymentIdController = TextEditingController();
final _cryptoAmountController = TextEditingController();
final _fiatAmountController = TextEditingController();
@@ -75,14 +110,11 @@ class SendFormState extends State<SendForm> {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).openalias_alert_title),
content: Text(S.of(context).openalias_alert_content(sendStore.recordName)),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () => Navigator.of(context).pop())
],
return AlertWithOneAction(
alertTitle: S.of(context).openalias_alert_title,
alertContent: S.of(context).openalias_alert_content(sendStore.recordName),
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
}
@@ -96,92 +128,20 @@ class SendFormState extends State<SendForm> {
final balanceStore = Provider.of<BalanceStore>(context);
final walletStore = Provider.of<WalletStore>(context);
final syncStore = Provider.of<SyncStore>(context);
final sendTemplateStore = Provider.of<SendTemplateStore>(context);
_setEffects(context);
return ScrollableWithBottomSection(
contentPadding: EdgeInsets.all(0),
return Container(
color: Theme.of(context).backgroundColor,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24),
content: Column(
children: [
Container(
padding: EdgeInsets.only(left: 38, right: 30),
decoration: BoxDecoration(
color: Theme.of(context).backgroundColor,
boxShadow: [
BoxShadow(
color: Palette.shadowGrey,
blurRadius: 10,
offset: Offset(0, 12),
)
],
border: Border(
top: BorderSide(
width: 1,
color: Theme.of(context)
.accentTextTheme
.subtitle
.backgroundColor))),
child: SizedBox(
height: 56,
width: double.infinity,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Observer(builder: (_) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(S.of(context).send_your_wallet,
style: TextStyle(
fontSize: 12, color: Palette.lightViolet)),
Text(walletStore.name,
style: TextStyle(
fontSize: 18,
color: Theme.of(context)
.accentTextTheme
.overline
.color,
height: 1.25)),
]);
}),
Observer(builder: (context) {
final savedDisplayMode = settingsStore.balanceDisplayMode;
final availableBalance =
savedDisplayMode == BalanceDisplayMode.hiddenBalance
? '---'
: balanceStore.unlockedBalance;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(S.of(context).xmr_available_balance,
style: TextStyle(
fontSize: 12,
color: Theme.of(context)
.accentTextTheme
.overline
.backgroundColor,
)),
Text(availableBalance,
style: TextStyle(
fontSize: 22,
color: Theme.of(context)
.accentTextTheme
.overline
.color,
height: 1.1)),
]);
})
],
),
),
),
Form(
key: _formKey,
child: Container(
padding:
EdgeInsets.only(left: 38, right: 33, top: 10, bottom: 30),
children: <Widget>[
TopPanel(
color: Theme.of(context).accentTextTheme.title.backgroundColor,
widget: Form(
key: _formKey,
child: Column(children: <Widget>[
AddressTextField(
controller: _addressController,
@@ -190,126 +150,126 @@ class SendFormState extends State<SendForm> {
onURIScanned: (uri) {
var address = '';
var amount = '';
var paymentId = '';
if (uri != null) {
address = uri.path;
amount = uri.queryParameters['tx_amount'];
paymentId = uri.queryParameters['tx_payment_id'];
} else {
address = uri.toString();
}
_addressController.text = address;
_cryptoAmountController.text = amount;
_paymentIdController.text = paymentId;
},
options: [
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook
],
buttonColor: Theme.of(context).accentTextTheme.title.color,
validator: (value) {
sendStore.validateAddress(value,
cryptoCurrency: CryptoCurrency.xmr);
return sendStore.errorMessage;
},
),
Padding(
padding: const EdgeInsets.only(top: 20),
child: TextFormField(
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context)
.accentTextTheme
.overline
.backgroundColor),
controller: _paymentIdController,
decoration: InputDecoration(
hintStyle: TextStyle(
fontSize: 14.0,
color: Theme.of(context).hintColor),
hintText: S.of(context).send_payment_id,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
width: 1.0))),
validator: (value) {
sendStore.validatePaymentID(value);
return sendStore.errorMessage;
}),
Observer(
builder: (_) {
return Padding(
padding: const EdgeInsets.only(top: 20),
child: TextFormField(
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
controller: _cryptoAmountController,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: true),
inputFormatters: [
BlacklistingTextInputFormatter(
RegExp('[\\-|\\ |\\,]'))
],
decoration: InputDecoration(
prefixIcon: Padding(
padding: EdgeInsets.only(top: 12),
child: Text('XMR:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color,
)),
),
suffixIcon: Padding(
padding: EdgeInsets.only(
bottom: 5
),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
width: MediaQuery.of(context).size.width/2,
alignment: Alignment.centerLeft,
child: Text(
' / ' + balanceStore.unlockedBalance,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.caption.color
)
),
),
Container(
height: 32,
width: 32,
margin: EdgeInsets.only(left: 12, bottom: 7, top: 4),
decoration: BoxDecoration(
color: Theme.of(context).accentTextTheme.title.color,
borderRadius: BorderRadius.all(Radius.circular(6))
),
child: InkWell(
onTap: () => sendStore.setSendAll(),
child: Center(
child: Text(S.of(context).all,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.caption.color
)
),
),
),
)
],
),
),
hintStyle: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color),
hintText: '0.0000',
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
validator: (value) {
sendStore.validateXMR(
value, balanceStore.unlockedBalance);
return sendStore.errorMessage;
}),
);
}
),
Padding(
padding: const EdgeInsets.only(top: 20),
child: TextFormField(
style: TextStyle(
fontSize: 18.0,
color: Theme.of(context)
.accentTextTheme
.overline
.color),
controller: _cryptoAmountController,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: true),
inputFormatters: [
BlacklistingTextInputFormatter(
RegExp('[\\-|\\ |\\,]'))
],
decoration: InputDecoration(
prefixIcon: Padding(
padding: EdgeInsets.only(top: 12),
child: Text('XMR:',
style: TextStyle(
fontSize: 18,
color: Theme.of(context)
.accentTextTheme
.overline
.color,
)),
),
suffixIcon: Container(
width: 1,
padding: EdgeInsets.only(top: 0),
child: Center(
child: InkWell(
onTap: () => sendStore.setSendAll(),
child: Text(S.of(context).all,
style: TextStyle(
fontSize: 10,
color: Theme.of(context)
.accentTextTheme
.overline
.decorationColor))),
),
),
hintStyle: TextStyle(
fontSize: 18.0,
color: Theme.of(context).hintColor),
hintText: '0.0000',
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
width: 1.0))),
validator: (value) {
sendStore.validateXMR(
value, balanceStore.unlockedBalance);
return sendStore.errorMessage;
}),
),
Padding(
padding: const EdgeInsets.only(top: 20),
child: TextFormField(
style: TextStyle(
fontSize: 18.0,
color: Theme.of(context)
.accentTextTheme
.overline
.color),
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color),
controller: _fiatAmountController,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: true),
@@ -323,124 +283,188 @@ class SendFormState extends State<SendForm> {
child: Text(
'${settingsStore.fiatCurrency.toString()}:',
style: TextStyle(
fontSize: 18,
color: Theme.of(context)
.accentTextTheme
.overline
.color,
fontSize: 16,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color,
)),
),
hintStyle: TextStyle(
fontSize: 18.0,
color: Theme.of(context).hintColor),
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.caption.color),
hintText: '0.00',
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
color: Theme.of(context).dividerColor,
width: 1.0)))),
),
Padding(
padding: const EdgeInsets.only(top: 12.0, bottom: 10),
padding: const EdgeInsets.only(top: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(S.of(context).send_estimated_fee,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.accentTextTheme
.overline
.backgroundColor,
fontSize: 12,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color,
)),
Text(
'${calculateEstimatedFee(priority: settingsStore.transactionPriority)} XMR',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme
.overline
.backgroundColor,
fontSize: 12,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color,
))
],
),
),
SizedBox(
width: double.infinity,
child: Text(
S.of(context).send_priority(
settingsStore.transactionPriority.toString()),
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color,
height: 1.3)),
),
)
]),
),
),
Padding(
padding: EdgeInsets.only(
top: 32,
left: 24,
bottom: 24
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).send_templates,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.caption.color
),
)
],
),
),
Container(
height: 40,
width: double.infinity,
padding: EdgeInsets.only(left: 24),
child: Observer(
builder: (_) {
final itemCount = sendTemplateStore.templates.length + 1;
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: itemCount,
itemBuilder: (context, index) {
if (index == 0) {
return GestureDetector(
onTap: () => Navigator.of(context)
.pushNamed(Routes.sendTemplate),
child: Container(
padding: EdgeInsets.only(right: 10),
child: DottedBorder(
borderType: BorderType.RRect,
dashPattern: [8, 4],
color: Theme.of(context).accentTextTheme.title.backgroundColor,
strokeWidth: 2,
radius: Radius.circular(20),
child: Container(
height: 40,
width: 75,
padding: EdgeInsets.only(left: 10, right: 10),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20)),
color: Colors.transparent,
),
child: Text(
S.of(context).send_new,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.caption.color
),
),
)
),
),
);
}
index -= 1;
final template = sendTemplateStore.templates[index];
return TemplateTile(
to: template.name,
amount: template.amount,
from: template.cryptoCurrency,
onTap: () {
_addressController.text = template.address;
_cryptoAmountController.text = template.amount;
getOpenaliasRecord(context);
}
);
}
);
}
),
)
],
),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Observer(builder: (_) {
return LoadingPrimaryButton(
onPressed: syncStore.status is SyncedSyncStatus
? () async {
// Hack. Don't ask me.
FocusScope.of(context).requestFocus(FocusNode());
// Hack. Don't ask me.
FocusScope.of(context).requestFocus(FocusNode());
if (_formKey.currentState.validate()) {
await showDialog<void>(
context: context,
builder: (dialogContext) {
return AlertDialog(
title: Text(
S.of(context).send_creating_transaction),
content: Text(S.of(context).confirm_sending),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).send),
onPressed: () async {
await Navigator.of(dialogContext)
.popAndPushNamed(Routes.auth,
arguments: (bool
isAuthenticatedSuccessfully,
AuthPageState auth) {
if (!isAuthenticatedSuccessfully) {
return;
}
if (_formKey.currentState.validate()) {
await showDialog<void>(
context: context,
builder: (dialogContext) {
return AlertWithTwoActions(
alertTitle: S.of(context).send_creating_transaction,
alertContent: S.of(context).confirm_sending,
leftButtonText: S.of(context).send,
rightButtonText: S.of(context).cancel,
actionLeftButton: () async {
await Navigator.of(dialogContext)
.popAndPushNamed(Routes.auth,
arguments: (bool
isAuthenticatedSuccessfully,
AuthPageState auth) {
if (!isAuthenticatedSuccessfully) {
return;
}
Navigator.of(auth.context).pop();
Navigator.of(auth.context).pop();
sendStore.createTransaction(
address: _addressController.text,
paymentId:
_paymentIdController.text);
});
}),
FlatButton(
child: Text(S.of(context).cancel),
onPressed: () =>
Navigator.of(context).pop())
],
);
});
}
}
: null,
sendStore.createTransaction(
address: _addressController.text,
paymentId: '');
});
},
actionRightButton: () =>
Navigator.of(context).pop()
);
});
}
}
: null,
text: S.of(context).send,
color: Theme.of(context).accentTextTheme.button.backgroundColor,
borderColor:
Theme.of(context).accentTextTheme.button.decorationColor,
color: Colors.blue,
textColor: Colors.white,
isLoading: sendStore.state is CreatingTransaction ||
sendStore.state is TransactionCommiting);
}));
sendStore.state is TransactionCommiting,
isDisabled: !(syncStore.status is SyncedSyncStatus),
);
}),
),
);
}
void _setEffects(BuildContext context) {
@@ -462,6 +486,20 @@ class SendFormState extends State<SendForm> {
}
});
reaction((_) => sendStore.address, (String address) {
if (address != _addressController.text) {
_addressController.text = address;
}
});
_addressController.addListener(() {
final address = _addressController.text;
if (sendStore.address != address) {
sendStore.changeAddress(address);
}
});
_fiatAmountController.addListener(() {
final fiatAmount = _fiatAmountController.text;
@@ -484,14 +522,11 @@ class SendFormState extends State<SendForm> {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).error),
content: Text(state.error),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () => Navigator.of(context).pop())
],
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
});
@@ -502,23 +537,25 @@ class SendFormState extends State<SendForm> {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).confirm_sending),
content: Text(S.of(context).commit_transaction_amount_fee(
sendStore.pendingTransaction.amount,
sendStore.pendingTransaction.fee)),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () {
Navigator.of(context).pop();
sendStore.commitTransaction();
}),
FlatButton(
child: Text(S.of(context).cancel),
onPressed: () => Navigator.of(context).pop(),
)
],
return ConfirmSendingAlert(
alertTitle: S.of(context).confirm_sending,
amount: S.of(context).send_amount,
amountValue: sendStore.pendingTransaction.amount,
fee: S.of(context).send_fee,
feeValue: sendStore.pendingTransaction.fee,
leftButtonText: S.of(context).ok,
rightButtonText: S.of(context).cancel,
actionLeftButton: () {
Navigator.of(context).pop();
sendStore.commitTransaction();
showDialog<void>(
context: context,
builder: (BuildContext context) {
return SendingAlert(sendStore: sendStore);
}
);
},
actionRightButton: () => Navigator.of(context).pop()
);
});
});
@@ -526,23 +563,8 @@ class SendFormState extends State<SendForm> {
if (state is TransactionCommitted) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).sending),
content: Text(S.of(context).transaction_sent),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () {
_addressController.text = '';
_cryptoAmountController.text = '';
Navigator.of(context)..pop()..pop();
})
],
);
});
_addressController.text = '';
_cryptoAmountController.text = '';
});
}
});
@@ -0,0 +1,277 @@
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/address_text_field.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/src/stores/balance/balance_store.dart';
import 'package:cake_wallet/src/stores/send/send_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/top_panel.dart';
import 'package:cake_wallet/src/stores/send_template/send_template_store.dart';
class SendTemplatePage extends BasePage {
@override
String get title => S.current.send_title;
@override
Color get backgroundLightColor => Palette.lavender;
@override
Color get backgroundDarkColor => PaletteDark.lightNightBlue;
@override
bool get resizeToAvoidBottomPadding => false;
@override
Widget body(BuildContext context) => SendTemplateForm();
}
class SendTemplateForm extends StatefulWidget {
@override
SendTemplateFormState createState() => SendTemplateFormState();
}
class SendTemplateFormState extends State<SendTemplateForm> {
final _nameController = TextEditingController();
final _addressController = TextEditingController();
final _cryptoAmountController = TextEditingController();
final _fiatAmountController = TextEditingController();
final _formKey = GlobalKey<FormState>();
bool _effectsInstalled = false;
@override
void dispose() {
_nameController.dispose();
_addressController.dispose();
_cryptoAmountController.dispose();
_fiatAmountController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final settingsStore = Provider.of<SettingsStore>(context);
final balanceStore = Provider.of<BalanceStore>(context);
final sendStore = Provider.of<SendStore>(context);
sendStore.settingsStore = settingsStore;
final sendTemplateStore = Provider.of<SendTemplateStore>(context);
_setEffects(context);
return Container(
color: Theme.of(context).backgroundColor,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24),
content: Column(
children: <Widget>[
TopPanel(
color: Theme.of(context).accentTextTheme.title.backgroundColor,
widget: Form(
key: _formKey,
child: Column(children: <Widget>[
TextFormField(
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color),
controller: _nameController,
decoration: InputDecoration(
hintStyle: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.caption.color),
hintText: S.of(context).send_name,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
validator: (value) {
sendTemplateStore.validateTemplate(value);
return sendTemplateStore.errorMessage;
},
),
Padding(
padding: EdgeInsets.only(top: 20),
child: AddressTextField(
controller: _addressController,
placeholder: S.of(context).send_monero_address,
onURIScanned: (uri) {
var address = '';
var amount = '';
if (uri != null) {
address = uri.path;
amount = uri.queryParameters['tx_amount'];
} else {
address = uri.toString();
}
_addressController.text = address;
_cryptoAmountController.text = amount;
},
options: [
AddressTextFieldOption.qrCode,
AddressTextFieldOption.addressBook
],
buttonColor: Theme.of(context).accentTextTheme.title.color,
validator: (value) {
sendTemplateStore.validateTemplate(value);
return sendTemplateStore.errorMessage;
},
),
),
Observer(
builder: (_) {
return Padding(
padding: const EdgeInsets.only(top: 20),
child: TextFormField(
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
controller: _cryptoAmountController,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: true),
inputFormatters: [
BlacklistingTextInputFormatter(
RegExp('[\\-|\\ |\\,]'))
],
decoration: InputDecoration(
prefixIcon: Padding(
padding: EdgeInsets.only(top: 12),
child: Text('XMR:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color,
)),
),
hintStyle: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color),
hintText: '0.0000',
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
),
);
}
),
Padding(
padding: const EdgeInsets.only(top: 20),
child: TextFormField(
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color),
controller: _fiatAmountController,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: true),
inputFormatters: [
BlacklistingTextInputFormatter(
RegExp('[\\-|\\ |\\,]'))
],
decoration: InputDecoration(
prefixIcon: Padding(
padding: EdgeInsets.only(top: 12),
child: Text(
'${settingsStore.fiatCurrency.toString()}:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color,
)),
),
hintStyle: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.caption.color),
hintText: '0.00',
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)))),
),
]),
),
),
],
),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: PrimaryButton(
onPressed: () {
if (_formKey.currentState.validate()) {
sendTemplateStore.addTemplate(
name: _nameController.text,
address: _addressController.text,
cryptoCurrency: 'XMR',
amount: _cryptoAmountController.text
);
sendTemplateStore.update();
Navigator.of(context).pop();
}
},
text: S.of(context).save,
color: Colors.blue,
textColor: Colors.white
),
),
);
}
void _setEffects(BuildContext context) {
if (_effectsInstalled) {
return;
}
final sendStore = Provider.of<SendStore>(context);
reaction((_) => sendStore.fiatAmount, (String amount) {
if (amount != _fiatAmountController.text) {
_fiatAmountController.text = amount;
}
});
reaction((_) => sendStore.cryptoAmount, (String amount) {
if (amount != _cryptoAmountController.text) {
_cryptoAmountController.text = amount;
}
});
_fiatAmountController.addListener(() {
final fiatAmount = _fiatAmountController.text;
if (sendStore.fiatAmount != fiatAmount) {
sendStore.changeFiatAmount(fiatAmount);
}
});
_cryptoAmountController.addListener(() {
final cryptoAmount = _cryptoAmountController.text;
if (sendStore.cryptoAmount != cryptoAmount) {
sendStore.changeCryptoAmount(cryptoAmount);
}
});
_effectsInstalled = true;
}
}
@@ -0,0 +1,100 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
class ConfirmSendingAlert extends BaseAlertDialog {
ConfirmSendingAlert({
@required this.alertTitle,
@required this.amount,
@required this.amountValue,
@required this.fee,
@required this.feeValue,
@required this.leftButtonText,
@required this.rightButtonText,
@required this.actionLeftButton,
@required this.actionRightButton,
this.alertBarrierDismissible = true
});
final String alertTitle;
final String amount;
final String amountValue;
final String fee;
final String feeValue;
final String leftButtonText;
final String rightButtonText;
final VoidCallback actionLeftButton;
final VoidCallback actionRightButton;
final bool alertBarrierDismissible;
@override
String get titleText => alertTitle;
@override
String get leftActionButtonText => leftButtonText;
@override
String get rightActionButtonText => rightButtonText;
@override
VoidCallback get actionLeft => actionLeftButton;
@override
VoidCallback get actionRight => actionRightButton;
@override
bool get barrierDismissible => alertBarrierDismissible;
@override
Widget content(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
amount,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
decoration: TextDecoration.none,
),
),
Text(
amountValue,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
decoration: TextDecoration.none,
),
)
],
),
Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
fee,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
decoration: TextDecoration.none,
),
),
Text(
feeValue,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
decoration: TextDecoration.none,
),
)
],
)
],
);
}
}
@@ -0,0 +1,104 @@
import 'dart:ui';
import 'package:cake_wallet/src/stores/send/sending_state.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/stores/send/send_store.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class SendingAlert extends StatefulWidget {
SendingAlert({@required this.sendStore});
final SendStore sendStore;
@override
SendingAlertState createState() => SendingAlertState(sendStore);
}
class SendingAlertState extends State<SendingAlert> {
SendingAlertState(this.sendStore);
final SendStore sendStore;
@override
Widget build(BuildContext context) {
return Observer(
builder: (_) {
final state = sendStore.state;
if (state is TransactionCommitted) {
return Stack(
children: <Widget>[
Container(
color: Theme.of(context).backgroundColor,
child: Center(
child: Image.asset(
'assets/images/birthday_cake.png'),
),
),
Center(
child: Padding(
padding: EdgeInsets.only(top: 220, left: 24, right: 24),
child: Text(
S.of(context).send_success,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color,
decoration: TextDecoration.none,
),
),
),
),
Positioned(
left: 24,
right: 24,
bottom: 24,
child: PrimaryButton(
onPressed: () => Navigator.of(context).pop(),
text: S.of(context).send_got_it,
color: Colors.blue,
textColor: Colors.white
)
)
],
);
}
return Stack(
children: <Widget>[
Container(
color: Theme.of(context).backgroundColor,
child: Center(
child: Image.asset(
'assets/images/birthday_cake.png'),
),
),
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(color: Theme.of(context).backgroundColor.withOpacity(0.25)),
child: Center(
child: Padding(
padding: EdgeInsets.only(top: 220),
child: Text(
S.of(context).send_sending,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color,
decoration: TextDecoration.none,
),
),
),
),
),
)
],
);
}
);
}
}
+68 -48
View File
@@ -5,6 +5,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/language.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
class ChangeLanguage extends BasePage {
@override
@@ -15,66 +16,85 @@ class ChangeLanguage extends BasePage {
final settingsStore = Provider.of<SettingsStore>(context);
final currentLanguage = Provider.of<Language>(context);
final currentColor = Theme.of(context).selectedRowColor;
final notCurrentColor = Theme.of(context).accentTextTheme.subhead.backgroundColor;
final currentColor = Colors.green;
final notCurrentColor = Theme.of(context).primaryTextTheme.title.color;
final shortDivider = Container(
height: 1,
padding: EdgeInsets.only(left: 24),
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: Container(
height: 1,
color: Theme.of(context).dividerColor,
),
);
final longDivider = Container(
height: 1,
color: Theme.of(context).dividerColor,
);
return Container(
padding: EdgeInsets.only(top: 10.0, bottom: 10.0),
padding: EdgeInsets.only(top: 10.0),
child: ListView.builder(
itemCount: languages.values.length,
itemBuilder: (BuildContext context, int index) {
final item = languages.values.elementAt(index);
final code = languages.keys.elementAt(index);
final isCurrent = settingsStore.languageCode == null
? false
: languages.keys.elementAt(index) ==
settingsStore.languageCode;
: code == settingsStore.languageCode;
return Container(
margin: EdgeInsets.only(top: 10.0, bottom: 10.0),
color: isCurrent ? currentColor : notCurrentColor,
child: ListTile(
title: Text(
languages.values.elementAt(index),
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color),
),
onTap: () async {
if (!isCurrent) {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
S.of(context).change_language,
textAlign: TextAlign.center,
),
content: Text(
S.of(context).change_language_to(
languages.values.elementAt(index)),
textAlign: TextAlign.center,
),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(S.of(context).cancel)),
FlatButton(
onPressed: () {
return Column(
children: <Widget>[
index == 0 ? longDivider : Offstage(),
Container(
padding: EdgeInsets.only(top: 4, bottom: 4),
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: ListTile(
contentPadding: EdgeInsets.only(left: 24, right: 24),
title: Text(
item,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w600,
color: isCurrent ? currentColor : notCurrentColor
),
),
trailing: isCurrent
? Icon(Icons.done, color: currentColor)
: Offstage(),
onTap: () async {
if (!isCurrent) {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).change_language,
alertContent: S.of(context).change_language_to(item),
leftButtonText: S.of(context).change,
rightButtonText: S.of(context).cancel,
actionLeftButton: () {
settingsStore.saveLanguageCode(
languageCode:
languages.keys.elementAt(index));
currentLanguage.setCurrentLanguage(
languages.keys.elementAt(index));
languageCode: code);
currentLanguage.setCurrentLanguage(code);
Navigator.of(context).pop();
},
child: Text(S.of(context).change)),
],
);
});
}
},
),
actionRightButton: () => Navigator.of(context).pop()
);
});
}
},
),
),
item == languages.values.last
? longDivider
: shortDivider
],
);
},
));
)
);
}
}
@@ -1,236 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/palette.dart';
import 'package:flutter/foundation.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/theme_changer.dart';
import 'package:cake_wallet/themes.dart';
import 'package:cake_wallet/generated/i18n.dart';
class EnterPinCode extends StatefulWidget {
const EnterPinCode(this.currentPinLength, this.currentPin);
final int currentPinLength;
final List<int> currentPin;
@override
EnterPinCodeState createState() =>
EnterPinCodeState(currentPinLength, currentPin);
}
class EnterPinCodeState extends State<EnterPinCode> {
EnterPinCodeState(this.pinLength, this.currentPin);
final _gridViewKey = GlobalKey();
final _closeButtonImage = Image.asset('assets/images/close_button.png');
final _closeButtonImageDarkTheme =
Image.asset('assets/images/close_button_dark_theme.png');
static final deleteIconImage = Image.asset('assets/images/delete_icon.png');
final int pinLength;
final List<int> currentPin;
List<int> pin;
double _aspectRatio = 0;
void _calcualteCurrentAspectRatio() {
final renderBox =
_gridViewKey.currentContext.findRenderObject() as RenderBox;
final cellWidth = renderBox.size.width / 3;
final cellHeight = renderBox.size.height / 4;
if (cellWidth > 0 && cellHeight > 0) {
_aspectRatio = cellWidth / cellHeight;
}
setState(() {});
}
@override
void initState() {
super.initState();
pin = List<int>.filled(pinLength, null);
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
}
void _afterLayout(dynamic _) => _calcualteCurrentAspectRatio();
@override
Widget build(BuildContext context) {
final _themeChanger = Provider.of<ThemeChanger>(context);
final _isDarkTheme = _themeChanger.getTheme() == Themes.darkTheme;
return Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
appBar: CupertinoNavigationBar(
leading: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
onPressed: () {
Navigator.pop(context, false);
},
child: _isDarkTheme
? _closeButtonImageDarkTheme
: _closeButtonImage),
),
backgroundColor: Theme.of(context).backgroundColor,
border: null),
body: SafeArea(
child: Container(
padding: EdgeInsets.only(left: 40.0, right: 40.0, bottom: 40.0),
child: Column(
children: <Widget>[
Spacer(flex: 2),
Text(S.of(context).enter_your_pin,
style: TextStyle(fontSize: 24, color: Palette.wildDarkBlue)),
Spacer(flex: 3),
Container(
width: 180,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(pinLength, (index) {
const size = 10.0;
final isFilled = pin[index] != null;
return Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
color:
isFilled ? Palette.deepPurple : Colors.transparent,
border: Border.all(color: Palette.wildDarkBlue),
));
}),
),
),
Spacer(flex: 3),
Flexible(
flex: 24,
child: Container(
key: _gridViewKey,
child: _aspectRatio > 0
? GridView.count(
crossAxisCount: 3,
childAspectRatio: _aspectRatio,
physics: const NeverScrollableScrollPhysics(),
children: List.generate(12, (index) {
if (index == 9) {
return Container(
margin: EdgeInsets.all(5.0),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _isDarkTheme
? PaletteDark.darkThemePinButton
: Palette.darkGrey,
),
);
} else if (index == 10) {
index = 0;
} else if (index == 11) {
return Container(
margin: EdgeInsets.all(5.0),
child: FlatButton(
onPressed: () {
_pop();
},
color: _isDarkTheme
? PaletteDark.darkThemePinButton
: Palette.darkGrey,
shape: CircleBorder(),
child: deleteIconImage,
),
);
} else {
index++;
}
return Container(
margin: EdgeInsets.all(5.0),
child: FlatButton(
onPressed: () {
_push(index);
},
color: _isDarkTheme
? PaletteDark.darkThemePinDigitButton
: Palette.creamyGrey,
shape: CircleBorder(),
child: Text('$index',
style: TextStyle(
fontSize: 23.0,
color: Palette.blueGrey)),
),
);
}),
)
: null))
],
),
)),
);
}
void _showIncorrectPinDialog(BuildContext context) async {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text(S.of(context).pin_is_incorrect),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
});
}
void _push(int num) {
if (_pinLength() >= pinLength) {
return;
}
for (var i = 0; i < pin.length; i++) {
if (pin[i] == null) {
setState(() => pin[i] = num);
break;
}
}
final currentPinLength = _pinLength();
if (currentPinLength == pinLength) {
if (listEquals<int>(pin, currentPin)) {
Navigator.pop(context, true);
} else {
Navigator.pop(context, false);
_showIncorrectPinDialog(context);
}
}
}
void _pop() {
if (_pinLength() == 0) {
return;
}
for (var i = pin.length - 1; i >= 0; i--) {
if (pin[i] != null) {
setState(() => pin[i] = null);
break;
}
}
}
int _pinLength() {
return pin.fold(0, (v, e) {
if (e != null) {
return v + 1;
}
return v;
});
}
}
+127 -120
View File
@@ -1,7 +1,6 @@
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/palette.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:provider/provider.dart';
@@ -17,7 +16,7 @@ import 'package:cake_wallet/src/screens/settings/attributes.dart';
import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
import 'package:cake_wallet/src/screens/settings/items/settings_item.dart';
import 'package:cake_wallet/src/screens/settings/items/item_headers.dart';
import 'package:cake_wallet/src/widgets/present_picker.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
// Settings widgets
import 'package:cake_wallet/src/screens/settings/widgets/settings_arrow_list_row.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_header_list_row.dart';
@@ -30,12 +29,6 @@ class SettingsPage extends BasePage {
@override
String get title => S.current.settings_title;
@override
bool get isModalBackButton => true;
@override
Color get backgroundColor => Palette.lightGrey2;
@override
Widget body(BuildContext context) {
return SettingsForm();
@@ -73,21 +66,6 @@ class SettingsFormState extends State<SettingsForm> {
settingsStore.setItemHeaders();
_items.addAll([
SettingsItem(title: ItemHeaders.nodes, attribute: Attributes.header),
SettingsItem(
onTaped: () => Navigator.of(context).pushNamed(Routes.nodeList),
title: ItemHeaders.currentNode,
widget: Observer(
builder: (_) => Text(
settingsStore.node == null ? '' : settingsStore.node.uri,
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 16.0,
color:
Theme.of(context).primaryTextTheme.subtitle.color),
)),
attribute: Attributes.widget),
SettingsItem(title: ItemHeaders.wallets, attribute: Attributes.header),
SettingsItem(
onTaped: () => _setBalance(context),
title: ItemHeaders.displayBalanceAs,
@@ -96,9 +74,8 @@ class SettingsFormState extends State<SettingsForm> {
settingsStore.balanceDisplayMode.toString(),
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 16.0,
color:
Theme.of(context).primaryTextTheme.subtitle.color),
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.caption.color),
)),
attribute: Attributes.widget),
SettingsItem(
@@ -109,9 +86,8 @@ class SettingsFormState extends State<SettingsForm> {
settingsStore.fiatCurrency.toString(),
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 16.0,
color:
Theme.of(context).primaryTextTheme.subtitle.color),
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.caption.color),
)),
attribute: Attributes.widget),
SettingsItem(
@@ -122,15 +98,14 @@ class SettingsFormState extends State<SettingsForm> {
settingsStore.transactionPriority.toString(),
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 16.0,
color:
Theme.of(context).primaryTextTheme.subtitle.color),
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.caption.color),
)),
attribute: Attributes.widget),
SettingsItem(
title: ItemHeaders.saveRecipientAddress,
attribute: Attributes.switcher),
SettingsItem(title: ItemHeaders.personal, attribute: Attributes.header),
SettingsItem(title: '', attribute: Attributes.header),
SettingsItem(
onTaped: () {
Navigator.of(context).pushNamed(Routes.auth,
@@ -166,7 +141,11 @@ class SettingsFormState extends State<SettingsForm> {
children: [
Text(S
.of(context)
.settings_transactions),
.settings_transactions,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.title.color
),
),
Checkbox(
value: settingsStore
.actionlistDisplayMode
@@ -183,7 +162,12 @@ class SettingsFormState extends State<SettingsForm> {
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(S.of(context).settings_trades),
Text(
S.of(context).settings_trades,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.title.color
),
),
Checkbox(
value: settingsStore
.actionlistDisplayMode
@@ -196,17 +180,14 @@ class SettingsFormState extends State<SettingsForm> {
],
child: Container(
height: 56,
padding: EdgeInsets.only(left: 20, right: 20),
padding: EdgeInsets.only(left: 24, right: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(S.of(context).settings_display_on_dashboard_list,
style: TextStyle(
fontSize: 16,
color: Theme.of(context)
.primaryTextTheme
.title
.color)),
fontSize: 14,
color: Theme.of(context).primaryTextTheme.title.color)),
Observer(builder: (_) {
var title = '';
@@ -233,17 +214,14 @@ class SettingsFormState extends State<SettingsForm> {
return Text(title,
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context)
.primaryTextTheme
.subtitle
.color));
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.caption.color));
})
]),
));
},
attribute: Attributes.rawWidget),
SettingsItem(title: ItemHeaders.support, attribute: Attributes.header),
SettingsItem(title: '', attribute: Attributes.header),
SettingsItem(
onTaped: () => _launchUrl(_emailUrl),
title: 'Email',
@@ -253,13 +231,13 @@ class SettingsFormState extends State<SettingsForm> {
SettingsItem(
onTaped: () => _launchUrl(_telegramUrl),
title: 'Telegram',
link: 't.me/cakewallet_bot',
link: 'Cake_Wallet',
image: _telegramImage,
attribute: Attributes.link),
SettingsItem(
onTaped: () => _launchUrl(_twitterUrl),
title: 'Twitter',
link: 'twitter.com/CakewalletXMR',
link: '@CakeWalletXMR',
image: _twitterImage,
attribute: Attributes.link),
SettingsItem(
@@ -268,18 +246,18 @@ class SettingsFormState extends State<SettingsForm> {
link: 'support@changenow.io',
image: _changeNowImage,
attribute: Attributes.link),
SettingsItem(
onTaped: () => _launchUrl(_morphUrl),
title: 'Morph',
link: 'support@morphtoken.com',
image: _morphImage,
attribute: Attributes.link),
SettingsItem(
onTaped: () => _launchUrl(_xmrToUrl),
title: 'XMR.to',
link: 'support@xmr.to',
image: _xmrBtcImage,
attribute: Attributes.link),
SettingsItem(
onTaped: () => _launchUrl(_morphUrl),
title: 'MorphToken',
link: 'support@morphtoken.com',
image: _morphImage,
attribute: Attributes.link),
SettingsItem(
onTaped: () {
Navigator.push(
@@ -345,87 +323,116 @@ class SettingsFormState extends State<SettingsForm> {
final settingsStore = Provider.of<SettingsStore>(context);
settingsStore.setItemHeaders();
return SingleChildScrollView(
child: Column(
children: <Widget>[
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: _items.length,
itemBuilder: (context, index) {
final item = _items[index];
bool _isDrawDivider = true;
final shortDivider = Container(
height: 1,
padding: EdgeInsets.only(left: 24),
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: Container(
height: 1,
color: Theme.of(context).dividerColor,
),
);
if (item.attribute == Attributes.header || item == _items.last) {
_isDrawDivider = false;
} else {
if (_items[index + 1].attribute == Attributes.header) {
_isDrawDivider = false;
}
}
final longDivider = Container(
height: 1,
color: Theme.of(context).dividerColor,
);
return Column(
children: <Widget>[
_getWidget(item),
_isDrawDivider
? Container(
color: Theme.of(context)
.accentTextTheme
.headline
.backgroundColor,
padding: EdgeInsets.only(
left: 20.0,
right: 20.0,
),
child: Divider(
color: Theme.of(context).dividerColor,
height: 1.0,
),
)
: Offstage()
],
);
}),
ListTile(
contentPadding: EdgeInsets.only(left: 20.0),
title: Text(
settingsStore.itemHeaders[ItemHeaders.version],
style: TextStyle(
fontSize: 14.0, color: Palette.wildDarkBlue)
),
)
],
));
return Container(
padding: EdgeInsets.only(top: 12),
child: SingleChildScrollView(
child: Column(
children: <Widget>[
longDivider,
ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: _items.length,
itemBuilder: (context, index) {
final item = _items[index];
Widget divider;
if (item.attribute == Attributes.header || item == _items.last) {
divider = longDivider;
} else if (_items[index + 1].attribute == Attributes.header){
divider = longDivider;
} else {
divider = shortDivider;
}
return Column(
children: <Widget>[
_getWidget(item),
divider
],
);
}),
Padding(
padding: EdgeInsets.only(top: 12),
child: ListTile(
title: Center(
child: Text(
settingsStore.itemHeaders[ItemHeaders.version],
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.caption.color)
),
),
),
)
],
)),
);
}
Future<void> _setBalance(BuildContext context) async {
final settingsStore = Provider.of<SettingsStore>(context);
final selectedDisplayMode =
await presentPicker(context, BalanceDisplayMode.all);
final items = BalanceDisplayMode.all;
final selectedItem = items.indexOf(settingsStore.balanceDisplayMode);
if (selectedDisplayMode != null) {
await settingsStore.setCurrentBalanceDisplayMode(
balanceDisplayMode: selectedDisplayMode);
}
await showDialog<void>(
builder: (_) => Picker(
items: items,
selectedAtIndex: selectedItem,
title: S.of(context).please_select,
mainAxisAlignment: MainAxisAlignment.center,
onItemSelected: (BalanceDisplayMode mode) async =>
await settingsStore.setCurrentBalanceDisplayMode(
balanceDisplayMode: mode)),
context: context);
}
Future<void> _setCurrency(BuildContext context) async {
final settingsStore = Provider.of<SettingsStore>(context);
final selectedCurrency = await presentPicker(context, FiatCurrency.all);
final items = FiatCurrency.all;
final selectedItem = items.indexOf(settingsStore.fiatCurrency);
if (selectedCurrency != null) {
await settingsStore.setCurrentFiatCurrency(currency: selectedCurrency);
}
await showDialog<void>(
builder: (_) => Picker(
items: items,
selectedAtIndex: selectedItem,
title: S.of(context).please_select,
mainAxisAlignment: MainAxisAlignment.center,
onItemSelected: (FiatCurrency currency) async =>
await settingsStore.setCurrentFiatCurrency(currency: currency)),
context: context);
}
Future<void> _setTransactionPriority(BuildContext context) async {
final settingsStore = Provider.of<SettingsStore>(context);
final selectedPriority =
await presentPicker(context, TransactionPriority.all);
final items = TransactionPriority.all;
final selectedItem = items.indexOf(settingsStore.transactionPriority);
if (selectedPriority != null) {
await settingsStore.setCurrentTransactionPriority(
priority: selectedPriority);
}
await showDialog<void>(
builder: (_) => Picker(
items: items,
selectedAtIndex: selectedItem,
title: S.of(context).please_select,
mainAxisAlignment: MainAxisAlignment.center,
onItemSelected: (TransactionPriority priority) async =>
await settingsStore.setCurrentTransactionPriority(
priority: priority)),
context: context);
}
}
@@ -8,21 +8,22 @@ class SettingsArrowListRow extends StatelessWidget {
final VoidCallback onTaped;
final String title;
final _cakeArrowImage = Image.asset('assets/images/cake_arrow.png');
@override
Widget build(BuildContext context) {
final settingsStore = Provider.of<SettingsStore>(context);
final _cakeArrowImage = Image.asset('assets/images/select_arrow.png',
color: Theme.of(context).primaryTextTheme.caption.color);
return Container(
color: Theme.of(context).accentTextTheme.headline.backgroundColor,
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: ListTile(
contentPadding: EdgeInsets.only(left: 20.0, right: 20.0),
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
title: Observer(
builder: (_) => Text(
settingsStore.itemHeaders[title],
style: TextStyle(
fontSize: 16.0,
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.title.color),
)),
trailing: _cakeArrowImage,
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/palette.dart';
import 'package:provider/provider.dart';
class SettingsHeaderListRow extends StatelessWidget {
@@ -16,7 +15,7 @@ class SettingsHeaderListRow extends StatelessWidget {
return Column(
children: <Widget>[
SizedBox(
height: 28.0,
height: 20.0,
),
Container(
padding: EdgeInsets.only(left: 20.0),
@@ -25,9 +24,12 @@ class SettingsHeaderListRow extends StatelessWidget {
children: <Widget>[
Observer(
builder: (_) => Text(
settingsStore.itemHeaders[title],
title.isNotEmpty
? settingsStore.itemHeaders[title]
: '',
style: TextStyle(
fontSize: 15.0, color: Palette.wildDarkBlue),
fontSize: 15.0,
color: Theme.of(context).primaryTextTheme.caption.color),
))
],
),
@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
class SettingsLinktListRow extends StatelessWidget {
SettingsLinktListRow(
@@ -13,9 +12,9 @@ class SettingsLinktListRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).accentTextTheme.headline.backgroundColor,
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: ListTile(
contentPadding: EdgeInsets.only(left: 20.0, right: 20.0),
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
@@ -26,8 +25,7 @@ class SettingsLinktListRow extends StatelessWidget {
child: Text(
title,
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.w500,
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.title.color),
),
)
@@ -35,7 +33,7 @@ class SettingsLinktListRow extends StatelessWidget {
),
trailing: Text(
link,
style: TextStyle(fontSize: 14.0, color: Palette.cakeGreen),
style: TextStyle(fontSize: 14.0, color: Colors.blue),
),
onTap: onTaped,
),
@@ -8,7 +8,7 @@ class SettingRawWidgetListRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).accentTextTheme.headline.backgroundColor,
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: widgetBuilder(context) ?? Container(),
);
}
@@ -61,13 +61,13 @@ class SettingsSwitchListRow extends StatelessWidget {
final settingsStore = Provider.of<SettingsStore>(context);
return Container(
color: Theme.of(context).accentTextTheme.headline.backgroundColor,
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: ListTile(
contentPadding: EdgeInsets.only(left: 20.0, right: 20.0),
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
title: Observer(
builder: (_) => Text(settingsStore.itemHeaders[title],
style: TextStyle(
fontSize: 16.0,
fontSize: 14,
color: Theme.of(context).primaryTextTheme.title.color)),
),
trailing: _getSwitch(context)),
@@ -15,9 +15,9 @@ class SettingsTextListRow extends StatelessWidget {
final settingsStore = Provider.of<SettingsStore>(context);
return Container(
color: Theme.of(context).accentTextTheme.headline.backgroundColor,
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: ListTile(
contentPadding: EdgeInsets.only(left: 20.0, right: 20.0),
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
@@ -26,7 +26,7 @@ class SettingsTextListRow extends StatelessWidget {
builder: (_) => Text(
settingsStore.itemHeaders[title],
style: TextStyle(
fontSize: 16.0,
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.title.color),
)),
),
@@ -7,6 +7,7 @@ import 'package:cake_wallet/src/screens/pin_code/pin_code.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
class SetupPinCodePage extends BasePage {
SetupPinCodePage({this.onPinCodeSetup});
@@ -58,36 +59,28 @@ class _SetupPinCodeFormState<WidgetType extends SetupPinCodeForm>
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
content: Text(S.of(context).setup_successful),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () {
Navigator.of(context).pop();
widget.onPinCodeSetup(context, pin);
reset();
},
),
],
return AlertWithOneAction(
alertTitle: S.current.setup_pin,
alertContent: S.of(context).setup_successful,
buttonText: S.of(context).ok,
buttonAction: () {
Navigator.of(context).pop();
widget.onPinCodeSetup(context, pin);
reset();
},
alertBarrierDismissible: false,
);
});
} else {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text(S.of(context).pin_is_incorrect),
actions: <Widget>[
FlatButton(
child: Text(S.of(context).ok),
onPressed: () {
Navigator.of(context).pop();
},
),
],
return AlertWithOneAction(
alertTitle: S.current.setup_pin,
alertContent: S.of(context).pin_is_incorrect,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
+33 -32
View File
@@ -3,15 +3,12 @@ import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_keys_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
class ShowKeysPage extends BasePage {
@override
bool get isModalBackButton => true;
@override
String get title => S.current.wallet_keys;
@@ -20,7 +17,7 @@ class ShowKeysPage extends BasePage {
final walletKeysStore = Provider.of<WalletKeysStore>(context);
return Container(
padding: EdgeInsets.only(top: 20.0, bottom: 20.0, left: 20, right: 20),
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Observer(
builder: (_) {
final keysMap = {
@@ -39,39 +36,43 @@ class ShowKeysPage extends BasePage {
}
return ListView.separated(
separatorBuilder: (_, __) => Container(
padding: EdgeInsets.only(left: 30.0, right: 20.0),
child: Divider(
color: Theme.of(context).dividerTheme.color,
height: 1.0)),
separatorBuilder: (context, index) => Container(
height: 1,
padding: EdgeInsets.only(left: 24),
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: Container(
height: 1,
color: Theme.of(context).dividerColor,
),
),
itemCount: keysMap.length,
itemBuilder: (BuildContext context, int index) {
final key = keysMap.keys.elementAt(index);
final value = keysMap.values.elementAt(index);
return ListTile(
contentPadding: EdgeInsets.only(
top: 10, bottom: 10, left: 30, right: 20),
onTap: () {
Clipboard.setData(ClipboardData(
text: keysMap.values.elementAt(index)));
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
S.of(context).copied_key_to_clipboard(key),
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.green,
duration: Duration(seconds: 1),
));
},
title: Text(key + ':', style: TextStyle(fontSize: 16.0)),
subtitle: Container(
padding: EdgeInsets.only(top: 5.0),
child: Text(value,
style: TextStyle(
fontSize: 16.0, color: Palette.wildDarkBlue)),
final isDrawTop = index == 0 ? true : false;
final isDrawBottom = index == keysMap.length - 1 ? true : false;
return GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(text: value));
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
S.of(context).copied_key_to_clipboard(key),
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.green,
duration: Duration(seconds: 1),
));
},
child: StandartListRow(
title: key + ':',
value: value,
isDrawTop: isDrawTop,
isDrawBottom: isDrawBottom,
),
);
});
},
));
@@ -1,3 +1,4 @@
import 'package:cake_wallet/src/domain/monero/subaddress.dart';
import 'package:mobx/mobx.dart';
import 'package:provider/provider.dart';
import 'package:flutter/cupertino.dart';
@@ -8,14 +9,18 @@ import 'package:cake_wallet/src/stores/subaddress_creation/subaddress_creation_s
import 'package:cake_wallet/src/stores/subaddress_creation/subaddress_creation_store.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
class NewSubaddressPage extends BasePage {
NewSubaddressPage({this.subaddress});
final Subaddress subaddress;
@override
String get title => S.current.new_subaddress_title;
@override
Widget body(BuildContext context) => NewSubaddressForm();
Widget body(BuildContext context) => NewSubaddressForm(subaddress);
@override
Widget build(BuildContext context) {
@@ -34,68 +39,92 @@ class NewSubaddressPage extends BasePage {
}
class NewSubaddressForm extends StatefulWidget {
NewSubaddressForm(this.subaddress);
final Subaddress subaddress;
@override
NewSubaddressFormState createState() => NewSubaddressFormState();
NewSubaddressFormState createState() => NewSubaddressFormState(subaddress);
}
class NewSubaddressFormState extends State<NewSubaddressForm> {
NewSubaddressFormState(this.subaddress);
final _formKey = GlobalKey<FormState>();
final _labelController = TextEditingController();
final Subaddress subaddress;
@override
void initState() {
if (subaddress != null) _labelController.text = subaddress.label;
super.initState();
}
@override
void dispose() {
_labelController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final subaddressCreationStore =
Provider.of<SubadrressCreationStore>(context);
_labelController.addListener(() {
if (_labelController.text.isNotEmpty) {
subaddressCreationStore.setDisabledStatus(false);
} else {
subaddressCreationStore.setDisabledStatus(true);
}
});
return Form(
key: _formKey,
child: Stack(children: <Widget>[
Center(
child: Padding(
padding: EdgeInsets.only(left: 35, right: 35),
child: TextFormField(
controller: _labelController,
decoration: InputDecoration(
hintStyle: TextStyle(color: Theme.of(context).hintColor),
hintText: S.of(context).new_subaddress_label_name,
focusedBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
width: 1.0))),
validator: (value) {
subaddressCreationStore.validateSubaddressName(value);
return subaddressCreationStore.errorMessage;
}),
),
),
Positioned(
bottom: 20,
left: 20,
right: 20,
child: Observer(
child: Container(
padding: EdgeInsets.all(24.0),
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: BaseTextFormField(
controller: _labelController,
hintText: S.of(context).new_subaddress_label_name,
validator: (value) {
subaddressCreationStore.validateSubaddressName(value);
return subaddressCreationStore.errorMessage;
}
)
)
),
Observer(
builder: (_) => LoadingPrimaryButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
await subaddressCreationStore.add(
label: _labelController.text);
Navigator.of(context).pop();
if (subaddress != null) {
await subaddressCreationStore.setLabel(
addressIndex: subaddress.id,
label: _labelController.text
);
} else {
await subaddressCreationStore.add(
label: _labelController.text);
}
}
},
text: S.of(context).new_subaddress_create,
color: Theme.of(context)
.accentTextTheme
.button
.backgroundColor,
borderColor: Theme.of(context)
.accentTextTheme
.button
.decorationColor,
text: subaddress != null
? S.of(context).rename
: S.of(context).new_subaddress_create,
color: Colors.green,
textColor: Colors.white,
isLoading:
subaddressCreationStore.state is SubaddressIsCreating),
))
]));
subaddressCreationStore.state is SubaddressIsCreating,
isDisabled: subaddressCreationStore.isDisabledStatus,
),
)
],
),
)
);
}
}
@@ -8,14 +8,12 @@ import 'package:cake_wallet/src/stores/exchange_trade/exchange_trade_store.dart'
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_row.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
class TradeDetailsPage extends BasePage {
@override
String get title => S.current.trade_details_title;
@override
bool get isModalBackButton => true;
String get title => S.current.trade_details_title;
@override
Widget body(BuildContext context) {
@@ -26,7 +24,7 @@ class TradeDetailsPage extends BasePage {
formatDefault: "dd.MM.yyyy, HH:mm");
return Container(
padding: EdgeInsets.only(top: 10.0, bottom: 10.0, left: 20, right: 15),
padding: EdgeInsets.only(top: 20, bottom: 20),
child: Observer(builder: (_) {
final trade = exchangeStore.trade;
final items = [
@@ -58,15 +56,22 @@ class TradeDetailsPage extends BasePage {
}
return ListView.separated(
separatorBuilder: (_, __) => Divider(
color: Theme.of(context).dividerTheme.color,
height: 1.0,
),
padding:
EdgeInsets.only(left: 25, top: 10, right: 25, bottom: 15),
separatorBuilder: (_, __) => Container(
height: 1,
padding: EdgeInsets.only(left: 24),
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: Container(
height: 1,
color: Theme.of(context).dividerColor,
),
),
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
final item = items[index];
final isDrawTop = index == 0 ? true : false;
final isDrawBottom = index == items.length - 1 ? true : false;
return GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(text: '${item.value}'));
@@ -80,7 +85,11 @@ class TradeDetailsPage extends BasePage {
);
},
child: StandartListRow(
title: '${item.title}', value: '${item.value}'));
title: '${item.title}',
value: '${item.value}',
isDrawTop: isDrawTop,
isDrawBottom: isDrawBottom,
));
});
}));
}
@@ -1,30 +0,0 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
class StandartListRow extends StatelessWidget {
StandartListRow({this.title, this.value});
final String title;
final String value;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 10, bottom: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(title,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryTextTheme.overline.color),
textAlign: TextAlign.left),
Padding(
padding: const EdgeInsets.only(top: 5),
child: Text(value,
style: TextStyle(fontSize: 14, color: Palette.wildDarkBlue)),
)
]),
);
}
}
@@ -7,17 +7,15 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/transaction_info.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_row.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/palette.dart';
class TransactionDetailsPage extends BasePage {
TransactionDetailsPage({this.transactionInfo});
final TransactionInfo transactionInfo;
@override
bool get isModalBackButton => true;
@override
String get title => S.current.transaction_details_title;
@@ -80,17 +78,24 @@ class TransactionDetailsFormState extends State<TransactionDetailsForm> {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(left: 20, right: 15, top: 10, bottom: 10),
padding: EdgeInsets.only(top: 20, bottom: 20),
child: ListView.separated(
separatorBuilder: (context, index) => Container(
height: 1,
color: Theme.of(context).dividerTheme.color,
),
padding: EdgeInsets.only(left: 25, top: 10, right: 25, bottom: 15),
height: 1,
padding: EdgeInsets.only(left: 24),
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: Container(
height: 1,
color: Theme.of(context).dividerColor,
),
),
itemCount: _items.length,
itemBuilder: (context, index) {
final item = _items[index];
final isDrawTop = index == 0 ? true : false;
final isDrawBottom = index == _items.length - 1 ? true : false;
return GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(text: item.value));
@@ -104,7 +109,11 @@ class TransactionDetailsFormState extends State<TransactionDetailsForm> {
);
},
child:
StandartListRow(title: '${item.title}:', value: item.value),
StandartListRow(
title: '${item.title}:',
value: item.value,
isDrawTop: isDrawTop,
isDrawBottom: isDrawBottom),
);
}),
);
+142 -89
View File
@@ -6,23 +6,14 @@ import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/domain/common/wallet_description.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/stores/wallet_list/wallet_list_store.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
import 'package:cake_wallet/src/screens/wallet_list/wallet_menu.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/screens/wallet_list/widgets/wallet_tile.dart';
class WalletListPage extends BasePage {
@override
bool get isModalBackButton => true;
@override
String get title => S.current.wallet_list_title;
@override
AppBarStyle get appBarStyle => AppBarStyle.withShadow;
@override
Widget body(BuildContext context) => WalletListBody();
}
@@ -33,92 +24,154 @@ class WalletListBody extends StatefulWidget {
}
class WalletListBodyState extends State<WalletListBody> {
final moneroIcon = Image.asset('assets/images/monero.png', height: 24, width: 24);
WalletListStore _walletListStore;
void presetMenuForWallet(WalletDescription wallet, bool isCurrentWallet,
BuildContext bodyContext) {
final walletMenu = WalletMenu(bodyContext);
final items = walletMenu.generateItemsForWalletMenu(isCurrentWallet);
showDialog<void>(
context: bodyContext,
builder: (_) => Picker(
items: items,
selectedAtIndex: -1,
title: S.of(context).wallet_menu,
onItemSelected: (String item) => walletMenu.action(
walletMenu.listItems.indexOf(item), wallet, isCurrentWallet)),
);
}
ScrollController scrollController = ScrollController();
@override
Widget build(BuildContext context) {
final walletStore = Provider.of<WalletStore>(context);
_walletListStore = Provider.of<WalletListStore>(context);
return ScrollableWithBottomSection(
content: Container(
padding: EdgeInsets.all(20),
child: Observer(
builder: (_) => ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (_, index) => Divider(
color: Theme.of(context).dividerTheme.color, height: 1.0),
itemCount: _walletListStore.wallets.length,
itemBuilder: (__, index) {
final wallet = _walletListStore.wallets[index];
final isCurrentWallet =
final newWalletImage = Image.asset('assets/images/new_wallet.png',
height: 12,
width: 12,
color: Palette.oceanBlue);
final restoreWalletImage = Image.asset('assets/images/restore_wallet.png',
height: 12,
width: 12,
color: Theme.of(context).primaryTextTheme.title.color);
return SafeArea(
child: Container(
padding: EdgeInsets.only(top: 16),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 20),
content: Container(
child: Observer(
builder: (_) => ListView.separated(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
separatorBuilder: (_, index) => Divider(
color: Theme.of(context).backgroundColor,
height: 16),
itemCount: _walletListStore.wallets.length,
itemBuilder: (__, index) {
final wallet = _walletListStore.wallets[index];
final screenWidth = MediaQuery.of(context).size.width;
final isCurrentWallet =
_walletListStore.isCurrentWallet(wallet);
return InkWell(
onTap: () =>
presetMenuForWallet(wallet, isCurrentWallet, context),
child: Container(
padding: EdgeInsets.only(left: 10.0, right: 10.0),
child: ListTile(
title: Text(
wallet.name,
style: TextStyle(
color: isCurrentWallet
? Palette.cakeGreen
: Theme.of(context)
.primaryTextTheme
.headline
.color,
fontSize: 18.0,
fontWeight: FontWeight.w600),
String shortAddress = '';
if (isCurrentWallet) {
shortAddress = walletStore.subaddress.address;
shortAddress = shortAddress.replaceRange(4, shortAddress.length - 4, '...');
}
final walletMenu = WalletMenu(context);
final items = walletMenu.generateItemsForWalletMenu(isCurrentWallet);
final colors = walletMenu.generateColorsForWalletMenu(isCurrentWallet);
final images = walletMenu.generateImagesForWalletMenu(isCurrentWallet);
return Container(
height: 108,
width: double.infinity,
child: CustomScrollView(
scrollDirection: Axis.horizontal,
controller: scrollController,
slivers: <Widget>[
SliverPersistentHeader(
pinned: false,
floating: true,
delegate: WalletTile(
min: screenWidth - 228,
max: screenWidth,
image: moneroIcon,
walletName: wallet.name,
walletAddress: shortAddress,
isCurrent: isCurrentWallet
),
trailing: isCurrentWallet
? Icon(
Icons.check,
color: Palette.cakeGreen,
size: 20.0,
)
: null)));
}),
),
),
bottomSection: Column(children: <Widget>[
PrimaryIconButton(
onPressed: () => Navigator.of(context).pushNamed(Routes.newWallet),
iconData: Icons.add,
color: Theme.of(context).primaryTextTheme.button.backgroundColor,
borderColor:
Theme.of(context).primaryTextTheme.button.decorationColor,
iconColor: Palette.violet,
iconBackgroundColor: Theme.of(context).primaryIconTheme.color,
text: S.of(context).wallet_list_create_new_wallet),
SizedBox(height: 10.0),
PrimaryIconButton(
onPressed: () =>
Navigator.of(context).pushNamed(Routes.restoreWalletOptions),
iconData: Icons.refresh,
text: S.of(context).wallet_list_restore_wallet,
color: Theme.of(context).accentTextTheme.button.backgroundColor,
borderColor:
Theme.of(context).accentTextTheme.button.decorationColor,
iconColor: Theme.of(context).primaryTextTheme.caption.color,
iconBackgroundColor: Theme.of(context).accentIconTheme.color)
]));
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
final item = items[index];
final color = colors[index];
final image = images[index];
final radius = index == 0 ? 12.0 : 0.0;
return GestureDetector(
onTap: () {
scrollController.animateTo(0.0, duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
walletMenu.action(
walletMenu.listItems.indexOf(item), wallet, isCurrentWallet);
},
child: Container(
height: 108,
width: 108,
color: Theme.of(context).backgroundColor,
child: Container(
padding: EdgeInsets.only(left: 5, right: 5),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(radius),
bottomLeft: Radius.circular(radius)
),
color: color
),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
image,
SizedBox(height: 5),
Text(
item,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
color: Colors.white
),
)
],
),
),
),
),
);
},
childCount: items.length
)
)
],
),
);
}),
),
),
bottomSection: Column(children: <Widget>[
PrimaryImageButton(
onPressed: () => Navigator.of(context).pushNamed(Routes.newWalletType),
image: newWalletImage,
text: S.of(context).wallet_list_create_new_wallet,
color: Colors.white,
textColor: Palette.oceanBlue,
borderColor: Palette.oceanBlue,
),
SizedBox(height: 10.0),
PrimaryImageButton(
onPressed: () =>
Navigator.of(context).pushNamed(Routes.restoreWalletOptions),
image: restoreWalletImage,
text: S.of(context).wallet_list_restore_wallet,
color: Theme.of(context).primaryTextTheme.overline.color,
textColor: Theme.of(context).primaryTextTheme.title.color)
])),
)
);
}
}
@@ -18,6 +18,20 @@ class WalletMenu {
S.current.rescan
];
final List<Color> listColors = [
Colors.blue,
Colors.orange,
Colors.red,
Colors.green
];
final List<Image> listImages = [
Image.asset('assets/images/load.png', height: 32, width: 32, color: Colors.white),
Image.asset('assets/images/eye_action.png', height: 32, width: 32, color: Colors.white),
Image.asset('assets/images/trash.png', height: 32, width: 32, color: Colors.white),
Image.asset('assets/images/scanner.png', height: 32, width: 32, color: Colors.white)
];
List<String> generateItemsForWalletMenu(bool isCurrentWallet) {
final items = List<String>();
@@ -29,6 +43,28 @@ class WalletMenu {
return items;
}
List<Color> generateColorsForWalletMenu(bool isCurrentWallet) {
final colors = List<Color>();
if (!isCurrentWallet) colors.add(listColors[0]);
if (isCurrentWallet) colors.add(listColors[1]);
if (!isCurrentWallet) colors.add(listColors[2]);
if (isCurrentWallet) colors.add(listColors[3]);
return colors;
}
List<Image> generateImagesForWalletMenu(bool isCurrentWallet) {
final images = List<Image>();
if (!isCurrentWallet) images.add(listImages[0]);
if (isCurrentWallet) images.add(listImages[1]);
if (!isCurrentWallet) images.add(listImages[2]);
if (isCurrentWallet) images.add(listImages[3]);
return images;
}
void action(int index, WalletDescription wallet, bool isCurrentWallet) {
final _walletListStore = Provider.of<WalletListStore>(context);
@@ -0,0 +1,145 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
class WalletTile extends SliverPersistentHeaderDelegate {
WalletTile({
@required this.min,
@required this.max,
@required this.image,
@required this.walletName,
@required this.walletAddress,
@required this.isCurrent
});
final double min;
final double max;
final Image image;
final String walletName;
final String walletAddress;
final bool isCurrent;
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
double opacity = 1 - shrinkOffset / (max - min);
opacity = opacity >= 0 ? opacity : 0;
double panelWidth = 12 * opacity;
panelWidth = panelWidth < 12 ? 0 : 12;
final currentColor = isCurrent
? Theme.of(context).accentTextTheme.caption.color
: Theme.of(context).backgroundColor;
return Stack(
fit: StackFit.expand,
overflow: Overflow.visible,
children: <Widget>[
Positioned(
top: 0,
right: max - 4,
child: Container(
height: 108,
width: 4,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topRight: Radius.circular(4), bottomRight: Radius.circular(4)),
color: currentColor
),
),
),
Positioned(
top: 0,
right: 12,
child: Container(
height: 108,
width: max - 16,
padding: EdgeInsets.only(left: 20, right: 20),
color: Theme.of(context).backgroundColor,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
image,
SizedBox(width: 10),
Text(
walletName,
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color
),
)
],
),
isCurrent ? SizedBox(height: 5) : Offstage(),
isCurrent
? Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(width: 34),
Text(
walletAddress,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).primaryTextTheme.caption.color
),
)
],
)
: Offstage()
],
),
),
),
Positioned(
top: 0,
right: 0,
child: Opacity(
opacity: opacity,
child: Container(
height: 108,
width: panelWidth,
padding: EdgeInsets.only(
top: 1,
left: 1,
bottom: 1
),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(12), bottomLeft: Radius.circular(12)),
color: Theme.of(context).accentTextTheme.subtitle.color
),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(12), bottomLeft: Radius.circular(12)),
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Theme.of(context).accentTextTheme.caption.backgroundColor,
Theme.of(context).accentTextTheme.caption.decorationColor
]
)
),
),
),
)
),
],
);
}
@override
double get maxExtent => max;
@override
double get minExtent => min;
@override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
}
+94 -99
View File
@@ -1,44 +1,17 @@
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart';
WalletType _selectedType;
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
class WelcomePage extends BasePage {
static const _aspectRatioImage = 1.26;
static const _baseWidth = 411.43;
final _image = Image.asset('assets/images/welcomeImg.png');
final _cakeLogo = Image.asset('assets/images/cake_logo.png');
final Map<String, WalletType> _picker =
walletTypes.fold(Map<String, WalletType>(), (acc, item) {
acc[walletTypeToString(item)] = item;
return acc;
});
static const aspectRatioImage = 1.25;
final welcomeImage = Image.asset('assets/images/welcome.png');
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) => _selectedType == null
? showDialog<void>(
builder: (_) => Picker(
items: walletTypes
.map((item) => walletTypeToString(item))
.toList(),
selectedAtIndex: -1,
title: 'Select wallet type',
pickerHeight: 510,
onItemSelected: (String item) {
print('before $_selectedType');
_selectedType = _picker[item];
print('after $_selectedType');
}),
context: context)
: null);
return Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
resizeToAvoidBottomPadding: false,
@@ -47,77 +20,99 @@ class WelcomePage extends BasePage {
@override
Widget body(BuildContext context) {
final _screenWidth = MediaQuery.of(context).size.width;
final textScaleFactor = _screenWidth < _baseWidth ? 0.76 : 1.0;
final newWalletImage = Image.asset('assets/images/new_wallet.png',
height: 12,
width: 12,
color: Palette.oceanBlue);
final restoreWalletImage = Image.asset('assets/images/restore_wallet.png',
height: 12,
width: 12,
color: Theme.of(context).primaryTextTheme.title.color);
return Column(children: <Widget>[
Stack(
alignment: Alignment.center,
children: <Widget>[
AspectRatio(
aspectRatio: _aspectRatioImage,
child: FittedBox(child: _image, fit: BoxFit.fill)),
Positioned(bottom: 0.0, child: _cakeLogo)
],
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(
S.of(context).welcome,
style: TextStyle(
fontSize: 30.0,
fontWeight: FontWeight.bold,
),
textScaleFactor: textScaleFactor,
textAlign: TextAlign.center,
return Container(
padding: EdgeInsets.only(top: 20),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 20),
content: Column(
children: <Widget>[
AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(child: welcomeImage, fit: BoxFit.fill)),
Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 40),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
S.of(context).welcome,
style: TextStyle(
fontSize: 18,
color: Theme.of(context).primaryTextTheme.caption.color,
),
textAlign: TextAlign.center,
),
Padding(
padding: EdgeInsets.only(top: 10),
child: Text(
S.of(context).cake_wallet,
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color,
),
textAlign: TextAlign.center,
),
),
Padding(
padding: EdgeInsets.only(top: 14),
child: Text(
S.of(context).first_wallet_text,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.caption.color,
),
textAlign: TextAlign.center,
),
),
],
),
Text(
S.of(context).first_wallet_text,
style: TextStyle(
fontSize: 22.0,
color: Palette.lightBlue,
),
textScaleFactor: textScaleFactor,
textAlign: TextAlign.center,
),
Text(
S.of(context).please_make_selection,
style: TextStyle(
fontSize: 16.0,
color: Palette.lightBlue,
),
textScaleFactor: textScaleFactor,
textAlign: TextAlign.center,
)
]),
),
Container(
padding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 20.0),
child: Column(children: <Widget>[
PrimaryButton(
onPressed: () {
Navigator.pushNamed(context, Routes.newWalletFromWelcome,
arguments: _selectedType);
},
text: S.of(context).create_new,
color:
Theme.of(context).primaryTextTheme.button.backgroundColor,
borderColor:
Theme.of(context).primaryTextTheme.button.decorationColor),
SizedBox(height: 10),
PrimaryButton(
onPressed: () {
Navigator.pushNamed(context, Routes.restoreOptions,
arguments: _selectedType);
},
color: Theme.of(context).accentTextTheme.caption.backgroundColor,
borderColor:
Theme.of(context).accentTextTheme.caption.decorationColor,
text: S.of(context).restore_wallet,
)
]))
]);
],
),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 20),
bottomSection: Column(children: <Widget>[
Text(
S.of(context).please_make_selection,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).primaryTextTheme.caption.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: Colors.white,
textColor: Palette.oceanBlue,
borderColor: Palette.oceanBlue,
),
),
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).primaryTextTheme.overline.color,
textColor: Theme.of(context).primaryTextTheme.title.color),
)
]),
),
);
}
}
@@ -16,6 +16,7 @@ abstract class AcountListStoreBase with Store {
AcountListStoreBase({@required WalletService walletService}) {
accounts = [];
isAccountCreating = false;
isDisabledStatus = true;
if (walletService.currentWallet != null) {
_onWalletChanged(walletService.currentWallet);
@@ -37,10 +38,18 @@ abstract class AcountListStoreBase with Store {
@observable
bool isAccountCreating;
@observable
bool isDisabledStatus;
AccountList _accountList;
StreamSubscription<Wallet> _onWalletChangeSubscription;
StreamSubscription<List<Account>> _onAccountsChangeSubscription;
@action
void setDisabledStatus(bool isDisabled) {
isDisabledStatus = isDisabled;
}
// @override
// void dispose() {
// _onWalletChangeSubscription.cancel();
@@ -12,11 +12,15 @@ class AddressBookStore = AddressBookStoreBase with _$AddressBookStore;
abstract class AddressBookStoreBase with Store {
AddressBookStoreBase({@required this.contacts}) {
updateContactList();
isDisabledStatus = true;
}
@observable
List<Contact> contactList;
@observable
bool isDisabledStatus;
@observable
bool isValid;
@@ -37,6 +41,11 @@ abstract class AddressBookStoreBase with Store {
@action
Future delete({Contact contact}) async => await contact.delete();
@action
void setDisabledStatus(bool isDisabled) {
isDisabledStatus = isDisabled;
}
void validateContactName(String value) {
const pattern = '''^[^`,'"]{1,32}\$''';
final regExp = RegExp(pattern);
+29 -8
View File
@@ -33,6 +33,14 @@ abstract class ExchangeStoreBase with Store {
provider = initialProvider;
depositCurrency = initialDepositCurrency;
receiveCurrency = initialReceiveCurrency;
/*isDepositAddressEnabled = !(depositCurrency == walletStore.type);
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);*/
isDepositAddressEnabled = true;
isReceiveAddressEnabled = true;
depositAmount = '';
receiveAmount = '';
depositAddress = '';
receiveAddress = '';
limitsState = LimitsInitialState();
tradeState = ExchangeTradeStateInitial();
_cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12;
@@ -63,6 +71,18 @@ abstract class ExchangeStoreBase with Store {
@observable
String receiveAmount;
@observable
String depositAddress;
@observable
String receiveAddress;
@observable
bool isDepositAddressEnabled;
@observable
bool isReceiveAddressEnabled;
@observable
bool isValid;
@@ -71,10 +91,6 @@ abstract class ExchangeStoreBase with Store {
Box<Trade> trades;
String depositAddress;
String receiveAddress;
WalletStore walletStore;
Limits limits;
@@ -93,12 +109,16 @@ abstract class ExchangeStoreBase with Store {
void changeDepositCurrency({CryptoCurrency currency}) {
depositCurrency = currency;
_onPairChange();
/*isDepositAddressEnabled = !(depositCurrency == walletStore.type);
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);*/
}
@action
void changeReceiveCurrency({CryptoCurrency currency}) {
receiveCurrency = currency;
_onPairChange();
/*isDepositAddressEnabled = !(depositCurrency == walletStore.type);
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);*/
}
@action
@@ -218,12 +238,13 @@ abstract class ExchangeStoreBase with Store {
void reset() {
depositAmount = '';
receiveAmount = '';
depositAddress = '';
receiveAddress = '';
provider = XMRTOExchangeProvider();
depositCurrency = CryptoCurrency.xmr;
receiveCurrency = CryptoCurrency.btc;
loadLimits();
depositAddress = depositCurrency == walletStore.type ? walletStore.address : '';
receiveAddress = receiveCurrency == walletStore.type ? walletStore.address : '';
/*isDepositAddressEnabled = !(depositCurrency == walletStore.type);
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);*/
_onPairChange();
}
List<ExchangeProvider> providersForCurrentPair() {
@@ -0,0 +1,40 @@
import 'dart:async';
import 'package:mobx/mobx.dart';
import 'package:hive/hive.dart';
import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
part 'exchange_template_store.g.dart';
class ExchangeTemplateStore = ExchangeTemplateBase with _$ExchangeTemplateStore;
abstract class ExchangeTemplateBase with Store {
ExchangeTemplateBase({this.templateSource}) {
templates = ObservableList<ExchangeTemplate>();
update();
}
@observable
ObservableList<ExchangeTemplate> templates;
Box<ExchangeTemplate> templateSource;
@action
void update() =>
templates.replaceRange(0, templates.length, templateSource.values.toList());
@action
Future addTemplate({String amount, String depositCurrency, String receiveCurrency,
String provider, String depositAddress, String receiveAddress}) async {
final template = ExchangeTemplate(
amount: amount,
depositCurrency: depositCurrency,
receiveCurrency: receiveCurrency,
provider: provider,
depositAddress: depositAddress,
receiveAddress: receiveAddress);
await templateSource.add(template);
}
@action
Future remove({ExchangeTemplate template}) async => await template.delete();
}
@@ -12,6 +12,7 @@ class NodeListStore = NodeListBase with _$NodeListStore;
abstract class NodeListBase with Store {
NodeListBase({this.nodesSource}) {
nodes = ObservableList<Node>();
disabledState = true;
_onNodesChangeSubscription = nodesSource.watch().listen((e) => update());
update();
}
@@ -25,6 +26,9 @@ abstract class NodeListBase with Store {
@observable
String errorMessage;
@observable
bool disabledState;
Box<Node> nodesSource;
StreamSubscription<BoxEvent> _onNodesChangeSubscription;
@@ -61,6 +65,11 @@ abstract class NodeListBase with Store {
@action
Future reset() async => await resetToDefault(nodesSource);
@action
void setDisabledState(bool isDisable) {
disabledState = isDisable;
}
Future<bool> isNodeOnline(Node node) async {
try {
return await node.requestNode(node.uri,
+15
View File
@@ -45,6 +45,9 @@ abstract class SendStoreBase with Store {
@observable
String cryptoAmount;
@observable
String address;
@observable
bool isValid;
@@ -157,6 +160,18 @@ abstract class SendStoreBase with Store {
}
}
@action
void changeAddress(String address) {
this.address = address;
}
@action
void clear() {
address = '';
cryptoAmount = '';
fiatAmount = '';
}
Future<bool> isOpenaliasRecord(String name) async {
final _openaliasRecord = await OpenaliasRecord
.fetchAddressAndName(OpenaliasRecord.formatDomainName(name));
@@ -0,0 +1,48 @@
import 'dart:async';
import 'package:mobx/mobx.dart';
import 'package:hive/hive.dart';
import 'package:cake_wallet/src/domain/common/template.dart';
import 'package:cake_wallet/generated/i18n.dart';
part 'send_template_store.g.dart';
class SendTemplateStore = SendTemplateBase with _$SendTemplateStore;
abstract class SendTemplateBase with Store {
SendTemplateBase({this.templateSource}) {
templates = ObservableList<Template>();
update();
}
@observable
ObservableList<Template> templates;
@observable
bool isValid;
@observable
String errorMessage;
Box<Template> templateSource;
@action
void update() =>
templates.replaceRange(0, templates.length, templateSource.values.toList());
@action
Future addTemplate({String name, String address, String cryptoCurrency, String amount}) async {
final template = Template(name: name, address: address,
cryptoCurrency: cryptoCurrency, amount: amount);
await templateSource.add(template);
}
@action
Future remove({Template template}) async => await template.delete();
void validateTemplate(String value) {
const pattern = '''^[^`,'"]{1,106}\$''';
final regExp = RegExp(pattern);
isValid = regExp.hasMatch(value);
errorMessage = isValid ? null : S.current.error_text_template;
}
}
@@ -17,6 +17,7 @@ class SubadrressCreationStore = SubadrressCreationStoreBase
abstract class SubadrressCreationStoreBase with Store {
SubadrressCreationStoreBase({@required WalletService walletService}) {
state = SubaddressCreationStateInitial();
isDisabledStatus = true;
if (walletService.currentWallet != null) {
_onWalletChanged(walletService.currentWallet);
@@ -26,6 +27,7 @@ abstract class SubadrressCreationStoreBase with Store {
walletService.onWalletChange.listen(_onWalletChanged);
}
@observable
SubaddressCreationState state;
@observable
@@ -34,11 +36,19 @@ abstract class SubadrressCreationStoreBase with Store {
@observable
String errorMessage;
@observable
bool isDisabledStatus;
SubaddressList _subaddressList;
StreamSubscription<Wallet> _onWalletChangeSubscription;
StreamSubscription<Account> _onAccountChangeSubscription;
Account _account;
@action
void setDisabledStatus(bool isDisabled) {
isDisabledStatus = isDisabled;
}
// @override
// void dispose() {
// _onWalletChangeSubscription.cancel();
@@ -61,6 +71,20 @@ abstract class SubadrressCreationStoreBase with Store {
}
}
Future<void> setLabel({int addressIndex, String label}) async {
try {
state = SubaddressIsCreating();
await _subaddressList.setLabelSubaddress(
accountIndex: _account.id,
addressIndex: addressIndex,
label: label
);
state = SubaddressCreatedSuccessfully();
} catch (e) {
state = SubaddressCreationFailure(error: e.toString());
}
}
Future<void> _onWalletChanged(Wallet wallet) async {
if (wallet is MoneroWallet) {
_account = wallet.account;
@@ -16,6 +16,7 @@ abstract class WalletCreationStoreBase with Store {
@required this.walletListService,
@required this.sharedPreferences}) {
state = WalletCreationStateInitial();
isDisabledStatus = true;
}
final AuthenticationStore authStore;
@@ -31,6 +32,9 @@ abstract class WalletCreationStoreBase with Store {
@observable
bool isValid;
@observable
bool isDisabledStatus;
@action
Future create({String name, String language}) async {
state = WalletCreationStateInitial();
@@ -45,6 +49,11 @@ abstract class WalletCreationStoreBase with Store {
}
}
@action
void setDisabledStatus(bool isDisabled) {
isDisabledStatus = isDisabled;
}
void validateWalletName(String value) {
const pattern = '^[a-zA-Z0-9_]{1,15}\$';
final regExp = RegExp(pattern);
@@ -20,6 +20,7 @@ abstract class WalleRestorationStoreBase with Store {
@required this.walletListService,
@required this.sharedPreferences}) {
state = WalletRestorationStateInitial();
disabledState = true;
}
final AuthenticationStore authStore;
@@ -38,6 +39,9 @@ abstract class WalleRestorationStoreBase with Store {
@observable
List<MnemoticItem> seed;
@observable
bool disabledState;
@action
Future restoreFromSeed({String name, String seed, int restoreHeight}) async {
state = WalletRestorationStateInitial();
@@ -109,6 +113,11 @@ abstract class WalleRestorationStoreBase with Store {
return seed.fold('', (acc, item) => acc + ' ' + item.toString());
}
@action
void setDisabledState(bool isDisable) {
disabledState = isDisable;
}
void validateWalletName(String value) {
const pattern = '^[a-zA-Z0-9_]{1,15}\$';
final regExp = RegExp(pattern);
+34 -13
View File
@@ -1,6 +1,5 @@
import 'package:cake_wallet/routes.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/contact.dart';
import 'package:cake_wallet/src/domain/monero/subaddress.dart';
@@ -19,6 +18,8 @@ class AddressTextField extends StatelessWidget {
],
this.onURIScanned,
this.focusNode,
this.isBorderExist = true,
this.buttonColor,
this.validator});
static const prefixIconWidth = 34.0;
@@ -31,6 +32,8 @@ class AddressTextField extends StatelessWidget {
final Function(Uri) onURIScanned;
final List<AddressTextFieldOption> options;
final FormFieldValidator<String> validator;
final bool isBorderExist;
final Color buttonColor;
FocusNode focusNode;
@override
@@ -40,6 +43,10 @@ class AddressTextField extends StatelessWidget {
enabled: isActive,
controller: controller,
focusNode: focusNode,
style: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.title.color
),
decoration: InputDecoration(
suffixIcon: SizedBox(
width: prefixIconWidth * options.length +
@@ -58,9 +65,9 @@ class AddressTextField extends StatelessWidget {
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Palette.wildDarkBlueWithOpacity,
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(8))),
BorderRadius.all(Radius.circular(6))),
child: Image.asset('assets/images/qr_code_icon.png')),
))
],
@@ -76,11 +83,11 @@ class AddressTextField extends StatelessWidget {
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Palette.wildDarkBlueWithOpacity,
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(8))),
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/address_book_icon.png')),
'assets/images/open_book.png')),
))
],
if (this
@@ -95,9 +102,9 @@ class AddressTextField extends StatelessWidget {
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: Palette.wildDarkBlueWithOpacity,
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(8))),
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/receive_icon_raw.png')),
))
@@ -105,13 +112,27 @@ class AddressTextField extends StatelessWidget {
],
),
),
hintStyle: TextStyle(color: Theme.of(context).hintColor),
hintStyle: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.caption.color
),
hintText: placeholder ?? S.current.widgets_address,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
focusedBorder: isBorderExist
? UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))
: InputBorder.none,
disabledBorder: isBorderExist
? UnderlineInputBorder(
borderSide:
BorderSide(color: Theme.of(context).focusColor, width: 1.0)),
BorderSide(color: Theme.of(context).dividerColor, width: 1.0))
: InputBorder.none,
enabledBorder: isBorderExist
? UnderlineInputBorder(
borderSide:
BorderSide(color: Theme.of(context).dividerColor, width: 1.0))
: InputBorder.none,
),
validator: validator,
);
@@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
class AlertWithOneAction extends BaseAlertDialog {
AlertWithOneAction({
@required this.alertTitle,
@required this.alertContent,
@required this.buttonText,
@required this.buttonAction,
this.alertBarrierDismissible = true
});
final String alertTitle;
final String alertContent;
final String buttonText;
final VoidCallback buttonAction;
final bool alertBarrierDismissible;
@override
String get titleText => alertTitle;
@override
String get contentText => alertContent;
@override
bool get barrierDismissible => alertBarrierDismissible;
@override
Widget actionButtons(BuildContext context) {
return Container(
width: 300,
height: 52,
padding: EdgeInsets.only(left: 12, right: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24)
),
color: Colors.white
),
child: ButtonTheme(
minWidth: double.infinity,
child: FlatButton(
onPressed: buttonAction,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Text(
buttonText,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Colors.blue,
decoration: TextDecoration.none,
),
)),
),
);
}
}
@@ -0,0 +1,38 @@
import 'dart:ui';
import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
import 'package:flutter/cupertino.dart';
class AlertWithTwoActions extends BaseAlertDialog {
AlertWithTwoActions({
@required this.alertTitle,
@required this.alertContent,
@required this.leftButtonText,
@required this.rightButtonText,
@required this.actionLeftButton,
@required this.actionRightButton,
this.alertBarrierDismissible = true
});
final String alertTitle;
final String alertContent;
final String leftButtonText;
final String rightButtonText;
final VoidCallback actionLeftButton;
final VoidCallback actionRightButton;
final bool alertBarrierDismissible;
@override
String get titleText => alertTitle;
@override
String get contentText => alertContent;
@override
String get leftActionButtonText => leftButtonText;
@override
String get rightActionButtonText => rightButtonText;
@override
VoidCallback get actionLeft => actionLeftButton;
@override
VoidCallback get actionRight => actionRightButton;
@override
bool get barrierDismissible => alertBarrierDismissible;
}
+176
View File
@@ -0,0 +1,176 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
class BaseAlertDialog extends StatelessWidget {
String get titleText => '';
String get contentText => '';
String get leftActionButtonText => '';
String get rightActionButtonText => '';
VoidCallback get actionLeft => () {};
VoidCallback get actionRight => () {};
bool get barrierDismissible => true;
Widget title(BuildContext context) {
return Text(
titleText,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color,
decoration: TextDecoration.none,
),
);
}
Widget content(BuildContext context) {
return Text(
contentText,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color,
decoration: TextDecoration.none,
),
);
}
Widget actionButtons(BuildContext context) {
return Container(
width: 300,
height: 52,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24)
),
color: Colors.white
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(left: 12, right: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24)),
),
child: ButtonTheme(
minWidth: double.infinity,
child: FlatButton(
onPressed: actionLeft,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Text(
leftActionButtonText,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Colors.blue,
decoration: TextDecoration.none,
),
)),
),
)
),
Container(
height: 52,
width: 1,
color: Colors.grey.withOpacity(0.2),
),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 12, right: 12),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(bottomRight: Radius.circular(24)),
),
child: ButtonTheme(
minWidth: double.infinity,
child: FlatButton(
onPressed: actionRight,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: Text(
rightActionButtonText,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Colors.red,
decoration: TextDecoration.none,
),
)),
),
)
)
],
),
);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => barrierDismissible
? Navigator.of(context).pop()
: null,
child: Container(
color: Colors.transparent,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
child: Center(
child: GestureDetector(
onTap: () => null,
child: Container(
width: 300,
height: 257,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(24)),
color: Theme.of(context).accentTextTheme.title.backgroundColor
),
child: Column(
children: <Widget>[
Container(
width: 300,
height: 77,
padding: EdgeInsets.only(left: 24, right: 24),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24)
),
),
child: Center(
child: title(context),
),
),
Container(
width: 300,
height: 1,
color: Theme.of(context).dividerColor,
),
Container(
width: 300,
height: 127,
padding: EdgeInsets.all(24),
child: Center(
child: content(context),
),
),
actionButtons(context)
],
),
),
),
),
),
),
),
);
}
}
+81
View File
@@ -0,0 +1,81 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class BaseTextFormField extends StatelessWidget {
BaseTextFormField({
this.controller,
this.keyboardType = TextInputType.text,
this.textInputAction = TextInputAction.done,
this.textAlign = TextAlign.start,
this.autovalidate = false,
this.hintText = '',
this.maxLines = 1,
this.inputFormatters,
this.textColor,
this.hintColor,
this.borderColor,
this.prefix,
this.suffix,
this.suffixIcon,
this.enabled = true,
this.validator
});
final TextEditingController controller;
final TextInputType keyboardType;
final TextInputAction textInputAction;
final TextAlign textAlign;
final bool autovalidate;
final String hintText;
final int maxLines;
final List<TextInputFormatter> inputFormatters;
final Color textColor;
final Color hintColor;
final Color borderColor;
final Widget prefix;
final Widget suffix;
final Widget suffixIcon;
final bool enabled;
final FormFieldValidator<String> validator;
@override
Widget build(BuildContext context) {
return TextFormField(
controller: controller,
keyboardType: keyboardType,
textInputAction: textInputAction,
textAlign: textAlign,
autovalidate: autovalidate,
maxLines: maxLines,
inputFormatters: inputFormatters,
enabled: enabled,
style: TextStyle(
fontSize: 16.0,
color: textColor ?? Theme.of(context).primaryTextTheme.title.color
),
decoration: InputDecoration(
prefix: prefix,
suffix: suffix,
suffixIcon: suffixIcon,
hintStyle: TextStyle(
color: hintColor ?? Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: hintText,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? Theme.of(context).dividerColor,
width: 1.0
)
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? Theme.of(context).dividerColor,
width: 1.0
)
)
),
validator: validator,
);
}
}
+23 -11
View File
@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/monero/get_height_by_date.dart';
import 'package:cake_wallet/palette.dart';
class BlockchainHeightWidget extends StatefulWidget {
BlockchainHeightWidget({GlobalKey key}) : super(key: key);
@@ -37,19 +36,27 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
child: Container(
padding: EdgeInsets.only(top: 20.0, bottom: 10.0),
child: TextFormField(
style: TextStyle(fontSize: 14.0),
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
controller: restoreHeightController,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: false),
decoration: InputDecoration(
hintStyle: TextStyle(color: Theme.of(context).hintColor),
hintStyle: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: S.of(context).widgets_restore_from_blockheight,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor, width: 1.0))),
color: Theme.of(context).dividerColor,
width: 1.0))),
),
))
],
@@ -72,18 +79,23 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
onTap: () => _selectDate(context),
child: IgnorePointer(
child: TextFormField(
style: TextStyle(fontSize: 14.0),
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
decoration: InputDecoration(
hintStyle:
TextStyle(color: Theme.of(context).hintColor),
hintStyle: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: S.of(context).widgets_restore_from_date,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen,
width: 2.0)),
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
color: Theme.of(context).dividerColor,
width: 1.0))),
controller: dateController,
validator: (value) {
+3 -14
View File
@@ -1,8 +1,5 @@
import 'package:cake_wallet/theme_changer.dart';
import 'package:cake_wallet/themes.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class NavBar extends StatelessWidget implements ObstructingPreferredSizeWidget {
factory NavBar(
@@ -11,16 +8,13 @@ class NavBar extends StatelessWidget implements ObstructingPreferredSizeWidget {
Widget middle,
Widget trailing,
Color backgroundColor}) {
final _themeChanger = Provider.of<ThemeChanger>(context);
final _isDarkTheme = _themeChanger.getTheme() == Themes.darkTheme;
return NavBar._internal(
leading: leading,
middle: middle,
trailing: trailing,
height: _height,
backgroundColor:
_isDarkTheme ? Theme.of(context).backgroundColor : backgroundColor);
backgroundColor: backgroundColor);
}
factory NavBar.withShadow(
@@ -29,20 +23,15 @@ class NavBar extends StatelessWidget implements ObstructingPreferredSizeWidget {
Widget middle,
Widget trailing,
Color backgroundColor}) {
final _themeChanger = Provider.of<ThemeChanger>(context);
final _isDarkTheme = _themeChanger.getTheme() == Themes.darkTheme;
return NavBar._internal(
leading: leading,
middle: middle,
trailing: trailing,
height: 80,
backgroundColor:
_isDarkTheme ? Theme.of(context).backgroundColor : backgroundColor,
backgroundColor: backgroundColor,
decoration: BoxDecoration(
color: _isDarkTheme
? Theme.of(context).backgroundColor
: backgroundColor,
color: backgroundColor,
boxShadow: [
BoxShadow(
color: Color.fromRGBO(132, 141, 198, 0.11),

Some files were not shown because too many files have changed in this diff Show More