General changes. CAKE-1.

This commit is contained in:
M
2020-07-06 23:09:03 +03:00
160 changed files with 5492 additions and 4033 deletions
@@ -3,4 +3,7 @@ import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart';
const bitcoinAmountDivider = 100000000;
double bitcoinAmountToDouble({int amount}) =>
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
int doubleToBitcoinAmount(double amount) =>
(amount * bitcoinAmountDivider).toInt();
+6
View File
@@ -22,6 +22,12 @@ class Contact extends HiveObject {
CryptoCurrency get type => CryptoCurrency.deserialize(raw: raw);
@override
bool operator ==(Object o) => o is Contact && o.key == key;
@override
int get hashCode => key.hashCode;
void updateCryptoCurrency({@required CryptoCurrency currency}) =>
raw = currency.raw;
}
@@ -1,3 +1,5 @@
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -14,7 +16,6 @@ Future defaultSettingsMigration(
final currentVersion =
sharedPreferences.getInt('current_default_settings_migration_version') ??
0;
if (currentVersion >= version) {
return;
}
@@ -28,10 +29,11 @@ Future defaultSettingsMigration(
switch (version) {
case 1:
await sharedPreferences.setString(
'current_fiat_currency', FiatCurrency.usd.toString());
SettingsStoreBase.currentFiatCurrencyKey, FiatCurrency.usd.toString());
await sharedPreferences.setInt(
'current_fee_priority', TransactionPriority.standart.raw);
await sharedPreferences.setInt('current_balance_display_mode',
SettingsStoreBase.currentTransactionPriorityKey, TransactionPriority.standart.raw);
await sharedPreferences.setInt(
SettingsStoreBase.currentBalanceDisplayModeKey,
BalanceDisplayMode.availableBalance.raw);
await sharedPreferences.setBool('save_recipient_address', true);
await resetToDefault(nodes);
@@ -44,6 +46,10 @@ Future defaultSettingsMigration(
await replaceDefaultNode(
sharedPreferences: sharedPreferences, nodes: nodes);
break;
case 3:
await updateNodeTypes(nodes: nodes);
await addBitcoinElectrumServerList(nodes: nodes);
break;
default:
break;
@@ -87,9 +93,11 @@ Future<void> changeCurrentNodeToDefault(
final timeZone = DateTime.now().timeZoneOffset.inHours;
String nodeUri = '';
if (timeZone >= 1) { // Eurasia
if (timeZone >= 1) {
// Eurasia
nodeUri = 'xmr-node-eu.cakewallet.com:18081';
} else if (timeZone <= -4) { // America
} else if (timeZone <= -4) {
// America
nodeUri = 'xmr-node-usa-east.cakewallet.com:18081';
}
@@ -121,3 +129,17 @@ Future<void> replaceDefaultNode(
await changeCurrentNodeToDefault(
sharedPreferences: sharedPreferences, nodes: nodes);
}
Future<void> updateNodeTypes({@required Box<Node> nodes}) async {
nodes.values.forEach((node) async {
if (node.type == null) {
node.type = WalletType.monero;
await node.save();
}
});
}
Future<void> addBitcoinElectrumServerList({@required Box<Node> nodes}) async {
final serverList = await loadElectrumServerList();
await nodes.addAll(serverList);
}
+36 -5
View File
@@ -2,18 +2,22 @@ import 'package:flutter/foundation.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:hive/hive.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:cake_wallet/src/domain/common/digest_request.dart';
part 'node.g.dart';
@HiveType(typeId: 1)
class Node extends HiveObject {
Node({@required this.uri, this.login, this.password});
Node({@required this.uri, @required WalletType type, this.login, this.password}) {
this.type = type;
}
Node.fromMap(Map map)
: uri = (map['uri'] ?? '') as String,
login = map['login'] as String,
password = map['password'] as String;
password = map['password'] as String,
typeRaw = map['typeRaw'] as int;
static const boxName = 'Nodes';
@@ -26,7 +30,29 @@ class Node extends HiveObject {
@HiveField(2)
String password;
Future<bool> requestNode(String uri, {String login, String password}) async {
@HiveField(3)
int typeRaw;
WalletType get type => deserializeFromInt(typeRaw);
set type(WalletType type) => typeRaw = serializeToInt(type);
Future<bool> requestNode() async {
try {
switch (type) {
case WalletType.monero:
return requestMoneroNode();
case WalletType.bitcoin:
return requestBitcoinElectrumServer();
default:
return false;
}
} catch (_) {
return false;
}
}
Future<bool> requestMoneroNode() async {
Map<String, dynamic> resBody;
if (login != null && password != null) {
@@ -38,12 +64,17 @@ class Node extends HiveObject {
final url = Uri.http(uri, '/json_rpc');
final headers = {'Content-type': 'application/json'};
final body =
json.encode({"jsonrpc": "2.0", "id": "0", "method": "get_info"});
json.encode({'jsonrpc': '2.0', 'id': '0', 'method': 'get_info'});
final response =
await http.post(url.toString(), headers: headers, body: body);
resBody = json.decode(response.body) as Map<String, dynamic>;
}
return !(resBody["result"]["offline"] as bool);
return !(resBody['result']['offline'] as bool);
}
Future<bool> requestBitcoinElectrumServer() async {
// FIXME: IMPLEMENT ME
return true;
}
}
+24 -4
View File
@@ -2,6 +2,7 @@ import 'package:flutter/services.dart';
import 'package:hive/hive.dart';
import "package:yaml/yaml.dart";
import 'package:cake_wallet/src/domain/common/node.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
Future<List<Node>> loadDefaultNodes() async {
final nodesRaw = await rootBundle.loadString('assets/node_list.yml');
@@ -16,15 +17,34 @@ Future<List<Node>> loadDefaultNodes() async {
}).toList();
}
Future<List<Node>> loadElectrumServerList() async {
final serverListRaw =
await rootBundle.loadString('assets/electrum_server_list.yml');
final serverList = loadYaml(serverListRaw) as YamlList;
return serverList.map((dynamic raw) {
if (raw is Map) {
final node = Node.fromMap(raw);
node?.type = WalletType.bitcoin;
return node;
}
return null;
}).toList();
}
Future resetToDefault(Box<Node> nodeSource) async {
final nodes = await loadDefaultNodes();
final enteties = Map<int, Node>();
final moneroNodes = await loadDefaultNodes();
final bitcoinElectrumServerList = await loadElectrumServerList();
final nodes = moneroNodes + bitcoinElectrumServerList;
final entities = <int, Node>{};
await nodeSource.clear();
for (var i = 0; i < nodes.length; i++) {
enteties[i] = nodes[i];
entities[i] = nodes[i];
}
await nodeSource.putAll(enteties);
await nodeSource.putAll(entities);
}
+5 -1
View File
@@ -20,15 +20,19 @@ int serializeToInt(WalletType type) {
switch (type) {
case WalletType.monero:
return 0;
case WalletType.bitcoin:
return 1;
default:
return -1;
}
}
WalletType deserializeToInt(int raw) {
WalletType deserializeFromInt(int raw) {
switch (raw) {
case 0:
return WalletType.monero;
case 1:
return WalletType.bitcoin;
default:
return null;
}
+1 -1
View File
@@ -23,7 +23,7 @@ void setReactions(
@required SyncStore syncStore,
@required WalletStore walletStore,
@required WalletService walletService,
@required AuthenticationStore authenticationStore,
// @required AuthenticationStore authenticationStore,
@required LoginStore loginStore}) {
connectToNode(settingsStore: settingsStore, walletStore: walletStore);
onSyncStatusChange(
-109
View File
@@ -1,109 +0,0 @@
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/stores/account_list/account_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/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});
final Account account;
@override
String get title => S.current.account;
@override
Widget body(BuildContext context) => AccountForm(account);
}
class AccountForm extends StatefulWidget {
AccountForm(this.account);
final Account account;
@override
AccountFormState createState() => AccountFormState();
}
class AccountFormState extends State<AccountForm> {
final _formKey = GlobalKey<FormState>();
final _textController = TextEditingController();
@override
void initState() {
if (widget.account != null) _textController.text = widget.account.label;
super.initState();
}
@override
void dispose() {
_textController.dispose();
super.dispose();
}
@override
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(
color: Theme.of(context).backgroundColor,
padding: EdgeInsets.all(24.0),
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: BaseTextFormField(
controller: _textController,
hintText: S.of(context).account,
validator: (value) {
accountListStore.validateAccountName(value);
return accountListStore.errorMessage;
},
)
)),
Observer(
builder: (_) => LoadingPrimaryButton(
onPressed: () async {
if (!_formKey.currentState.validate()) {
return;
}
if (widget.account != null) {
await accountListStore.renameAccount(
index: widget.account.id,
label: _textController.text);
} else {
await accountListStore.addAccount(
label: _textController.text);
}
Navigator.of(context).pop(_textController.text);
},
text:
widget.account != null ? S.of(context).rename : S.of(context).add,
color: Colors.green,
textColor: Colors.white,
isLoading: accountListStore.isAccountCreating,
isDisabled: accountListStore.isDisabledStatus,
))
],
),
),
);
}
}
@@ -1,293 +0,0 @@
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
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/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
String get title => S.current.address_book;
@override
Widget trailing(BuildContext context) {
if (!isEditable) {
return null;
}
final addressBookStore = Provider.of<AddressBookStore>(context);
return Container(
width: 32.0,
height: 32.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).accentTextTheme.title.backgroundColor
),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Icon(Icons.add,
color: Theme.of(context).primaryTextTheme.title.color,
size: 22.0),
ButtonTheme(
minWidth: 32.0,
height: 32.0,
child: FlatButton(
shape: CircleBorder(),
onPressed: () async {
await Navigator.of(context)
.pushNamed(Routes.addressBookAddContact);
await addressBookStore.updateContactList();
},
child: Offstage()),
)
],
));
}
@override
Widget body(BuildContext context) {
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: (_, __) => 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 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);
return;
}
final isCopied = await showNameAndAddressDialog(
context, contact.name, contact.address);
if (isCopied != null && isCopied) {
await Clipboard.setData(
ClipboardData(text: contact.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: 1500),
),
);
}
},
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
),
),
)
],
)
),
),
isDrawBottom
? Container(
width: double.infinity,
height: 1,
color: Theme.of(context).dividerColor,
)
: Offstage(),
],
),
);
return !isEditable
? content
: Slidable(
key: Key('${contact.key}'),
actionPane: SlidableDrawerActionPane(),
child: content,
secondaryActions: <Widget>[
IconSlideAction(
caption: S.of(context).edit,
color: Colors.blue,
icon: Icons.edit,
onTap: () async {
await Navigator.of(context).pushNamed(
Routes.addressBookAddContact,
arguments: contact);
await addressBookStore.updateContactList();
},
),
IconSlideAction(
caption: S.of(context).delete,
color: Colors.red,
icon: CupertinoIcons.delete,
onTap: () async {
await showAlertDialog(context)
.then((isDelete) async {
if (isDelete != null && isDelete) {
await addressBookStore.delete(
contact: contact);
await addressBookStore.updateContactList();
}
});
},
),
],
dismissal: SlidableDismissal(
child: SlidableDrawerDismissal(),
onDismissed: (actionType) async {
await addressBookStore.delete(contact: contact);
await addressBookStore.updateContactList();
},
onWillDismiss: (actionType) async {
return await showAlertDialog(context);
},
),
);
}),
));
}
Image _getCurrencyImage(CryptoCurrency currency) {
Image image;
switch (currency) {
case CryptoCurrency.xmr:
image = Image.asset('assets/images/monero.png', height: 24, width: 24);
break;
case CryptoCurrency.ada:
image = Image.asset('assets/images/ada.png', height: 24, width: 24);
break;
case CryptoCurrency.bch:
image = Image.asset('assets/images/bch.png', height: 24, width: 24);
break;
case CryptoCurrency.bnb:
image = Image.asset('assets/images/bnb.png', height: 24, width: 24);
break;
case CryptoCurrency.btc:
image = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
break;
case CryptoCurrency.dash:
image = Image.asset('assets/images/dash.png', height: 24, width: 24);
break;
case CryptoCurrency.eos:
image = Image.asset('assets/images/eos.png', height: 24, width: 24);
break;
case CryptoCurrency.eth:
image = Image.asset('assets/images/eth.png', height: 24, width: 24);
break;
case CryptoCurrency.ltc:
image = Image.asset('assets/images/litecoin.png', height: 24, width: 24);
break;
case CryptoCurrency.nano:
image = Image.asset('assets/images/nano.png', height: 24, width: 24);
break;
case CryptoCurrency.trx:
image = Image.asset('assets/images/trx.png', height: 24, width: 24);
break;
case CryptoCurrency.usdt:
image = Image.asset('assets/images/usdt.png', height: 24, width: 24);
break;
case CryptoCurrency.xlm:
image = Image.asset('assets/images/xlm.png', height: 24, width: 24);
break;
case CryptoCurrency.xrp:
image = Image.asset('assets/images/xrp.png', height: 24, width: 24);
break;
default:
image = null;
}
return image;
}
Future<bool> showAlertDialog(BuildContext context) async {
return await showDialog(
context: context,
builder: (BuildContext context) {
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)
);
});
}
Future<bool> showNameAndAddressDialog(
BuildContext context, String name, String address) async {
return await showDialog(
context: context,
builder: (BuildContext context) {
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)
);
});
}
}
@@ -1,234 +0,0 @@
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';
import 'package:cake_wallet/src/domain/common/contact.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/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/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});
final Contact contact;
@override
String get title => S.current.contact;
@override
Widget body(BuildContext context) => ContactForm(contact);
}
class ContactForm extends StatefulWidget {
ContactForm(this.contact);
final Contact contact;
@override
State<ContactForm> createState() => ContactFormState();
}
class ContactFormState extends State<ContactForm> {
final _formKey = GlobalKey<FormState>();
final _contactNameController = TextEditingController();
final _currencyTypeController = TextEditingController();
final _addressController = TextEditingController();
final currencies = CryptoCurrency.all;
CryptoCurrency _selectectCrypto;
@override
void initState() {
super.initState();
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();
_currencyTypeController.dispose();
_addressController.dispose();
super.dispose();
}
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);
_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(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();
}
),
context: context);
}
}
+1 -1
View File
@@ -125,7 +125,7 @@ abstract class BasePage extends StatelessWidget {
_isDarkTheme ? backgroundDarkColor : backgroundLightColor,
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
appBar: appBar(context),
body: SafeArea(child: body(context)),
body: body(context), //SafeArea(child: ),
floatingActionButton: floatingActionButton(context));
return rootWrapper?.call(context, root) ?? root;
@@ -0,0 +1,280 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
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/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
class ContactListPage extends BasePage {
ContactListPage(this.contactListViewModel, {this.isEditable = true});
final ContactListViewModel contactListViewModel;
final bool isEditable;
@override
String get title => S.current.address_book;
@override
Widget trailing(BuildContext context) {
if (!isEditable) {
return null;
}
return Container(
width: 32.0,
height: 32.0,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).accentTextTheme.title.backgroundColor),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Icon(Icons.add,
color: Theme.of(context).primaryTextTheme.title.color,
size: 22.0),
ButtonTheme(
minWidth: 32.0,
height: 32.0,
child: FlatButton(
shape: CircleBorder(),
onPressed: () async {
await Navigator.of(context)
.pushNamed(Routes.addressBookAddContact);
},
child: Offstage()),
)
],
));
}
@override
Widget body(BuildContext context) {
return Container(
color: Theme.of(context).backgroundColor,
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Observer(
builder: (_) {
return contactListViewModel.contacts.isNotEmpty
? 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: contactListViewModel.contacts.length,
itemBuilder: (BuildContext context, int index) {
final contact = contactListViewModel.contacts[index];
final image = _getCurrencyImage(contact.type);
final content = GestureDetector(
onTap: () async {
if (!isEditable) {
Navigator.of(context).pop(contact);
return;
}
final isCopied = await showNameAndAddressDialog(
context, contact.name, contact.address);
if (isCopied != null && isCopied) {
await Clipboard.setData(
ClipboardData(text: contact.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: 1500),
),
);
}
},
child: Column(
children: <Widget>[
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 ?? 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),
),
)
],
)),
),
],
),
);
return !isEditable
? content
: Slidable(
key: Key('${contact.key}'),
actionPane: SlidableDrawerActionPane(),
child: content,
secondaryActions: <Widget>[
IconSlideAction(
caption: S.of(context).edit,
color: Colors.blue,
icon: Icons.edit,
onTap: () async => await Navigator.of(context)
.pushNamed(Routes.addressBookAddContact,
arguments: contact),
),
IconSlideAction(
caption: S.of(context).delete,
color: Colors.red,
icon: CupertinoIcons.delete,
onTap: () async {
final isDelete =
await showAlertDialog(context) ?? false;
if (isDelete) {
await contactListViewModel
.delete(contact);
}
},
),
],
dismissal: SlidableDismissal(
child: SlidableDrawerDismissal(),
onDismissed: (actionType) async =>
await contactListViewModel.delete(contact),
onWillDismiss: (actionType) async =>
showAlertDialog(context),
),
);
})
: Center(
child: Text(
S.of(context).placeholder_contacts,
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context)
.primaryTextTheme
.caption
.color
.withOpacity(0.5),
fontSize: 14),
),
);
},
));
}
Image _getCurrencyImage(CryptoCurrency currency) {
Image image;
switch (currency) {
case CryptoCurrency.xmr:
image = Image.asset('assets/images/monero.png', height: 24, width: 24);
break;
case CryptoCurrency.ada:
image = Image.asset('assets/images/ada.png', height: 24, width: 24);
break;
case CryptoCurrency.bch:
image = Image.asset('assets/images/bch.png', height: 24, width: 24);
break;
case CryptoCurrency.bnb:
image = Image.asset('assets/images/bnb.png', height: 24, width: 24);
break;
case CryptoCurrency.btc:
image = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
break;
case CryptoCurrency.dash:
image = Image.asset('assets/images/dash.png', height: 24, width: 24);
break;
case CryptoCurrency.eos:
image = Image.asset('assets/images/eos.png', height: 24, width: 24);
break;
case CryptoCurrency.eth:
image = Image.asset('assets/images/eth.png', height: 24, width: 24);
break;
case CryptoCurrency.ltc:
image =
Image.asset('assets/images/litecoin.png', height: 24, width: 24);
break;
case CryptoCurrency.nano:
image = Image.asset('assets/images/nano.png', height: 24, width: 24);
break;
case CryptoCurrency.trx:
image = Image.asset('assets/images/trx.png', height: 24, width: 24);
break;
case CryptoCurrency.usdt:
image = Image.asset('assets/images/usdt.png', height: 24, width: 24);
break;
case CryptoCurrency.xlm:
image = Image.asset('assets/images/xlm.png', height: 24, width: 24);
break;
case CryptoCurrency.xrp:
image = Image.asset('assets/images/xrp.png', height: 24, width: 24);
break;
default:
image = null;
}
return image;
}
Future<bool> showAlertDialog(BuildContext context) async {
return await showDialog(
context: context,
builder: (BuildContext context) {
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));
});
}
Future<bool> showNameAndAddressDialog(
BuildContext context, String name, String address) async {
return await showDialog(
context: context,
builder: (BuildContext context) {
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
View File
@@ -0,0 +1,161 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/core/contact_name_validator.dart';
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
import 'package:cake_wallet/view_model/contact_list/contact_view_model_state.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.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/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
class ContactPage extends BasePage {
ContactPage(this.contactViewModel)
: _formKey = GlobalKey<FormState>(),
_nameController = TextEditingController(),
_addressController = TextEditingController(),
_currencyTypeController = TextEditingController() {
_nameController.text = contactViewModel.name;
_addressController.text = contactViewModel.address;
_nameController
.addListener(() => contactViewModel.name = _nameController.text);
_addressController
.addListener(() => contactViewModel.address = _addressController.text);
autorun((_) =>
_currencyTypeController.text = contactViewModel.currency.toString());
}
@override
String get title => S.current.contact;
final ContactViewModel contactViewModel;
final GlobalKey<FormState> _formKey;
final TextEditingController _nameController;
final TextEditingController _currencyTypeController;
final TextEditingController _addressController;
@override
Widget body(BuildContext context) {
final downArrow = Image.asset('assets/images/arrow_bottom_purple_icon.png',
color: Theme.of(context).dividerColor, height: 8);
reaction((_) => contactViewModel.state, (ContactViewModelState state) {
if (state is ContactCreationFailure) {
_onContactSavingFailure(context, state.error);
}
if (state is ContactSavingSuccessfully) {
_onContactSavedSuccessfully(context);
}
});
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: _nameController,
hintText: S.of(context).contact_name,
validator: ContactNameValidator()),
Padding(
padding: EdgeInsets.only(top: 20),
child: Container(
child: InkWell(
onTap: () => _presentCurrencyPicker(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: Observer(
builder: (_) => AddressTextField(
controller: _addressController,
options: [AddressTextFieldOption.qrCode],
validator: AddressValidator(
type: contactViewModel.currency),
)),
)
],
),
),
bottomSectionPadding:
EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Row(
children: <Widget>[
Expanded(
child: PrimaryButton(
onPressed: () => contactViewModel.reset(),
text: S.of(context).reset,
color: Colors.red,
textColor: Colors.white),
),
SizedBox(width: 20),
Expanded(
child: Observer(
builder: (_) => PrimaryButton(
onPressed: () async {
if (!_formKey.currentState.validate()) {
return;
}
await contactViewModel.save();
},
text: S.of(context).save,
color: Colors.green,
textColor: Colors.white,
isDisabled: !contactViewModel.isReady)))
],
)),
);
}
void _presentCurrencyPicker(BuildContext context) {
showDialog<void>(
builder: (_) => CurrencyPicker(
selectedAtIndex:
contactViewModel.currencies.indexOf(contactViewModel.currency),
items: contactViewModel.currencies,
title: S.of(context).please_select,
onItemSelected: (CryptoCurrency item) =>
contactViewModel.currency = item),
context: context);
}
void _onContactSavingFailure(BuildContext context, String error) {
showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.current.contact,
alertContent: error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
void _onContactSavedSuccessfully(BuildContext context) =>
Navigator.of(context).pop();
}
+129 -57
View File
@@ -1,78 +1,150 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/view_model/dashboard_view_model.dart';
import 'package:cake_wallet/src/screens/base_page.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 StatelessWidget {
class DashboardPage extends BasePage {
DashboardPage({@required this.walletViewModel});
final DashboardViewModel walletViewModel;
final _bodyKey = GlobalKey();
@override
Color get backgroundLightColor => Colors.transparent;
@override
Widget build(BuildContext context) =>
DashboardPageBody(key: _bodyKey, walletViewModel: walletViewModel);
}
class DashboardPageBody extends StatefulWidget {
DashboardPageBody({Key key, @required this.walletViewModel})
: super(key: key);
final DashboardViewModel walletViewModel;
Color get backgroundDarkColor => Colors.transparent;
@override
DashboardPageBodyState createState() => DashboardPageBodyState();
}
class DashboardPageBodyState extends State<DashboardPageBody> {
@override
Widget build(BuildContext context) {
final menuButton = Image.asset(
'assets/images/header.png',
color: Theme.of(context).primaryTextTheme.title.color,
);
return SafeArea(
child: Scaffold(
body: Container(
Widget Function(BuildContext, Widget) get rootWrapper =>
(BuildContext context, Widget scaffold) => 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(walletVM: widget.walletViewModel)),
SizedBox(height: 28),
Expanded(child: TradeHistoryPanel(dashboardViewModel: widget.walletViewModel))
],
),
),
], begin: Alignment.topLeft, end: Alignment.bottomRight)),
child: scaffold);
@override
Widget trailing(BuildContext context) {
final menuButton = Image.asset('assets/images/header.png',
color: Theme.of(context).primaryTextTheme.title.color);
return Container(
alignment: Alignment.centerRight,
child: SizedBox(
width: 24,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: () async {
await showDialog<void>(
builder: (_) => MenuWidget(
name: walletViewModel.name,
subname: walletViewModel.subname,
type: walletViewModel.type),
context: context);
},
child: menuButton),
),
);
}
final DashboardViewModel walletViewModel;
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 body(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
final transactionListMinHeight =
constraints.heightConstraints().maxHeight - 345 - 32;
return SingleChildScrollView(
child: Column(children: [
Container(
height: 345,
child: Column(children: [
Padding(
padding: EdgeInsets.only(left: 24, top: 10),
child: WalletCard(walletVM: walletViewModel)),
Container(
padding: EdgeInsets.only(left: 44, right: 44, top: 32),
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)),
],
),
)
])),
SizedBox(height: 32),
ConstrainedBox(
constraints: BoxConstraints(minHeight: transactionListMinHeight),
child: TradeHistoryPanel(dashboardViewModel: walletViewModel)),
// Column(children: [
// Text('1'),
// Text('2')
// ])
]));
});
}
}
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: 12),
child: Text(
title,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color.fromRGBO(140, 153, 201,
0.8) // Theme.of(context).primaryTextTheme.caption.color
),
),
)
],
),
);
}
@@ -16,10 +16,8 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
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;
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
final _themeChanger = Provider.of<ThemeChanger>(context);
Image filterButton;
@@ -29,74 +27,39 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
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))),
return ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24), topRight: Radius.circular(24)),
child: Container(
color: Colors.red,
// height: 75,
padding: EdgeInsets.only(top: 26, left: 20, right: 20, bottom: 10),
// color: Theme.of(context).backgroundColor,
child: Stack(
children: <Widget>[
Center(
child: Text(
S.of(context).transactions,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color),
)),
Positioned(
right: 0,
height: 36,
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(
@@ -135,27 +98,28 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
// .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'),
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
@@ -167,16 +131,15 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
// ExchangeProviderDescription
// .xmrto),
// )
]))),
PopupMenuItem(
value: 4,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text('Change.NOW'),
]))),
PopupMenuItem(
value: 4,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text('Change.NOW'),
// Checkbox(
// value: actionListStore
// .tradeFilterStore
@@ -188,16 +151,15 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
// ExchangeProviderDescription
// .changeNow),
// )
]))),
PopupMenuItem(
value: 5,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment
.spaceBetween,
children: [
Text('MorphToken'),
]))),
PopupMenuItem(
value: 5,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text('MorphToken'),
// Checkbox(
// value: actionListStore
// .tradeFilterStore
@@ -209,42 +171,37 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
// 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)));
])))
],
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) {
if (picked != null && picked.length == 2) {
// actionListStore.transactionFilterStore
// .changeStartDate(picked.first);
// actionListStore.transactionFilterStore
// .changeEndDate(picked.last);
}
}
},
),
)
],
),
),
)
)
],
}
}
},
)),
],
),
),
);
}
@override
double get maxExtent => 174;
double get maxExtent => 164;
@override
double get minExtent => 66;
@@ -252,12 +209,11 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
@override
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
Widget actionButton({
BuildContext context,
@required Image image,
@required String title,
@required String route}) {
Widget actionButton(
{BuildContext context,
@required Image image,
@required String title,
@required String route}) {
return Container(
width: MediaQuery.of(context).size.width,
child: Column(
@@ -276,23 +232,24 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context).primaryTextTheme.subhead.color,
shape: BoxShape.circle
),
shape: BoxShape.circle),
child: image,
),
),
Padding(
padding: EdgeInsets.only(top: 10),
padding: EdgeInsets.only(top: 12),
child: Text(
title,
style: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.caption.color
),
fontWeight: FontWeight.w600,
color: Color.fromRGBO(140, 153, 201,
0.8) // Theme.of(context).primaryTextTheme.caption.color
),
),
)
],
),
);
}
}
}
+197 -196
View File
@@ -1,25 +1,28 @@
import 'dart:async';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.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 {
MenuWidget({this.type, this.name, this.subname});
final WalletType type;
final String name;
final String subname;
@override
MenuWidgetState createState() => MenuWidgetState();
}
class MenuWidgetState extends State<MenuWidget> {
final moneroIcon = Image.asset('assets/images/monero.png');
final bitcoinIcon = Image.asset('assets/images/bitcoin.png');
final largeScreen = 731;
double menuWidth;
double screenWidth;
double screenHeight;
double opacity;
bool isDraw;
double headerHeight;
double tileHeight;
@@ -32,7 +35,6 @@ class MenuWidgetState extends State<MenuWidget> {
screenWidth = 0;
screenHeight = 0;
opacity = 0;
isDraw = false;
headerHeight = 120;
tileHeight = 75;
@@ -59,206 +61,205 @@ class MenuWidgetState extends State<MenuWidget> {
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 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),
return Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 24),
child: Container(
height: 60,
width: 4,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(2)),
color: Theme.of(context).hintColor),
)),
SizedBox(width: 12),
Expanded(
child: GestureDetector(
onTap: () => null,
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
),
)
)
],
),
width: menuWidth,
height: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
bottomLeft: Radius.circular(24)),
color: Theme.of(context).primaryTextTheme.display1.color),
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
bottomLeft: Radius.circular(24)),
child: 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>[
_iconFor(type: widget.type),
SizedBox(width: 16),
Expanded(
child: Container(
height: 40,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: widget.subname != null
? MainAxisAlignment.spaceBetween
: MainAxisAlignment.center,
children: <Widget>[
Text(
widget.name,
style: TextStyle(
color: Theme.of(context)
.primaryTextTheme
.title
.color,
decoration: TextDecoration.none,
fontFamily: 'Avenir Next',
fontSize: 20,
fontWeight: FontWeight.bold),
),
);
},
separatorBuilder: (_, index) =>
Container(
height: 1,
color: Theme.of(context).dividerColor,
),
itemCount: itemCount + 1)
: Offstage()
if (widget.subname != null)
Text(
widget.subname,
style: TextStyle(
color: Theme.of(context)
.primaryTextTheme
.caption
.color,
decoration: TextDecoration.none,
fontFamily: 'Avenir Next',
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: 'Avenir Next',
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: 'Avenir Next',
fontSize: 20,
fontWeight: FontWeight.bold),
))
],
),
),
);
},
separatorBuilder: (_, index) => Container(
height: 1,
color: Theme.of(context).dividerColor,
),
),
)
)
],
)
itemCount: itemCount + 1),
),
),
),
),
))
],
);
}
}
Image _iconFor({@required WalletType type}) {
switch (type) {
case WalletType.monero:
return moneroIcon;
case WalletType.bitcoin:
return bitcoinIcon;
default:
return null;
}
}
}
@@ -1,3 +1,6 @@
import 'package:cake_wallet/generated/i18n.dart';
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:flutter_mobx/flutter_mobx.dart';
@@ -15,11 +18,12 @@ import 'date_section_raw.dart';
import 'trade_row.dart';
import 'transaction_raw.dart';
import 'button_header.dart';
import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker;
class TradeHistoryPanel extends StatefulWidget {
TradeHistoryPanel({this.dashboardViewModel});
DashboardViewModel dashboardViewModel;
final DashboardViewModel dashboardViewModel;
@override
TradeHistoryPanelState createState() => TradeHistoryPanelState();
@@ -49,127 +53,270 @@ class TradeHistoryPanelState extends State<TradeHistoryPanel> {
@override
Widget build(BuildContext context) {
// final actionListStore = Provider.of<ActionListStore>(context);
// final settingsStore = Provider.of<SettingsStore>(context);
// AnimatedContainer(
// width: MediaQuery.of(context).size.width,
// height: panelHeight,
// duration: Duration(milliseconds: 1000),
// curve: Curves.fastOutSlowIn,
// child: )
final transactionDateFormat = DateFormat('HH:mm');
final _themeChanger = Provider.of<ThemeChanger>(context);
final filterButton = Image.asset(
_themeChanger.getTheme() == Themes.darkTheme
? 'assets/images/filter_button.png'
: 'assets/images/filter_light_button.png',
height: 36);
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 items = widget.dashboardViewModel.transactions;
final itemsCount = items.length + 1;
final symbol =
'\$'; // settingsStore.fiatCurrency.toString();
var freeSpaceHeight =
MediaQuery.of(context).size.height - 496;
return ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(20), topRight: Radius.circular(20)),
child: Container(
color: Colors.white,
child: Column(children: [
Container(
padding:
EdgeInsets.only(top: 32, left: 20, right: 20, bottom: 20),
color: Theme.of(context).backgroundColor,
child: Stack(
children: <Widget>[
SizedBox(height: 37), // Force stack height
Center(
child: Text(S.of(context).transactions,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
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 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)));
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 =
BalanceDisplayMode.all;
//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 =
BalanceDisplayMode.all;
//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));
})
if (picked != null && picked.length == 2) {
// actionListStore.transactionFilterStore
// .changeStartDate(picked.first);
// actionListStore.transactionFilterStore
// .changeEndDate(picked.last);
}
}
},
)),
],
)))); //,
),
),
widget.dashboardViewModel.transactions?.isNotEmpty ?? false
? ListView.separated(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: widget.dashboardViewModel.transactions.length,
itemBuilder: (_, index) {
final item =
widget.dashboardViewModel.transactions[index];
if (item is DateSectionItem) {
return DateSectionRaw(date: item.date);
}
if (item is TransactionListItem) {
final transaction = item.transaction;
final savedDisplayMode = BalanceDisplayMode.all;
//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) {
final trade = item.trade;
final savedDisplayMode = BalanceDisplayMode.all;
//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,
height: 1);
},
separatorBuilder: (_, __) =>
Container(height: 14, color: Colors.white),
)
: Padding(
padding: EdgeInsets.all(20),
child: Text('Your transactions will be displayed here!',
style: TextStyle(color: Colors.grey)))
]))); //,
}
}
@@ -24,27 +24,19 @@ class TransactionRow extends StatelessWidget {
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),
height: 41,
color: Theme.of(context).backgroundColor,
padding: EdgeInsets.only(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'),
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(
@@ -62,28 +54,43 @@ class TransactionRow extends StatelessWidget {
(isPending ? S.of(context).pending : ''),
style: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.title.color
)),
Text(direction == TransactionDirection.incoming
? formattedAmount
: '- ' + formattedAmount,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme
.title
.color)),
Text(
direction == TransactionDirection.incoming
? formattedAmount
: '- ' + formattedAmount,
style: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.title.color
))
fontWeight: FontWeight.w500,
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,
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))
fontSize: 14,
color: Theme.of(context)
.primaryTextTheme
.headline
.color))
]),
],
),
@@ -1,16 +1,12 @@
import 'dart:async';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/src/domain/common/balance_display_mode.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: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';
@@ -20,7 +16,7 @@ import 'package:cake_wallet/view_model/dashboard_view_model.dart';
class WalletCard extends StatefulWidget {
WalletCard({this.walletVM});
DashboardViewModel walletVM;
final DashboardViewModel walletVM;
@override
WalletCardState createState() => WalletCardState();
@@ -70,24 +66,20 @@ class WalletCardState extends State<WalletCard> {
width: double.infinity,
height: cardHeight,
alignment: Alignment.centerRight,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(14), bottomLeft: Radius.circular(14))),
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))
]),
topLeft: Radius.circular(14),
bottomLeft: Radius.circular(14)),
color: Theme.of(context).focusColor),
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
@@ -95,21 +87,18 @@ class WalletCardState extends State<WalletCard> {
width: cardWidth,
height: cardHeight,
color: Theme.of(context).cardColor,
child: InkWell(
onTap: () => setState(() => isFrontSide = !isFrontSide),
child: isFrontSide
? frontSide(colorsSync)
: backSide(colorsSync)),
child: isFrontSide
? frontSide(colorsSync)
: InkWell(
onTap: () => setState(() => isFrontSide = true),
child: 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,
@@ -121,9 +110,9 @@ class WalletCardState extends State<WalletCard> {
final status = widget.walletVM.status;
final statusText = status.title();
final progress = status.progress();
final indicatorWidth = progress * cardWidth;
final shortAddress = widget.walletVM.address
.replaceRange(4, widget.walletVM.address.length - 4, '...');
final indicatorOffset = progress * cardWidth;
final indicatorWidth =
progress <= 1 ? cardWidth - indicatorOffset : 0.0;
var descriptionText = '';
if (status is SyncingSyncStatus) {
@@ -137,37 +126,26 @@ class WalletCardState extends State<WalletCard> {
return Container(
width: cardWidth,
height: cardHeight,
color: Colors.white,
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
progress <= 1
? Positioned(
left: indicatorWidth,
left: indicatorOffset,
top: 0,
bottom: 0,
child: Container(
width: 1,
width: indicatorWidth,
height: cardHeight,
color: Theme.of(context).focusColor,
color: Color.fromRGBO(227, 238, 249, 1),
))
: Offstage(),
isDraw
? Positioned(
left: 20,
right: 20,
top: 30,
bottom: 30,
left: 24,
right: 24,
top: 32,
bottom: 24,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -180,7 +158,8 @@ class WalletCardState extends State<WalletCard> {
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InkWell(
onTap: () {},
onTap: () => Navigator.of(context)
.pushNamed(Routes.walletList),
child: Row(
children: <Widget>[
Text(
@@ -199,11 +178,13 @@ class WalletCardState extends State<WalletCard> {
),
),
SizedBox(height: 5),
if (widget.walletVM.subname?.isNotEmpty ?? false)
if (widget.walletVM.subname?.isNotEmpty ??
false)
Text(
widget.walletVM.subname,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme
.caption
@@ -211,26 +192,34 @@ class WalletCardState extends State<WalletCard> {
)
],
),
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),
),
InkWell(
onTap: () =>
setState(() => isFrontSide = false),
child: Container(
width: 98,
height: 32,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context)
.accentTextTheme
.subtitle
.backgroundColor,
border: Border.all(
color: Color.fromRGBO(
219, 231, 237, 1)),
// FIXME
borderRadius: BorderRadius.all(
Radius.circular(16))),
child: Text(
'Receive',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
)),
)
],
),
@@ -239,7 +228,7 @@ class WalletCardState extends State<WalletCard> {
key: _balanceObserverKey,
builder: (_) {
final balanceDisplayMode =
BalanceDisplayMode.fullBalance;
BalanceDisplayMode.availableBalance;
// settingsStore.balanceDisplayMode;
final symbol =
settingsStore.fiatCurrency.toString();
@@ -251,7 +240,7 @@ class WalletCardState extends State<WalletCard> {
balance = widget.walletVM.balance
.unlockedBalance ??
'0.0';
fiatBalance = '\$ 123.43';
fiatBalance = '\$ 0.00';
// '$symbol ${balanceStore.fiatUnlockedBalance}';
}
@@ -260,7 +249,7 @@ class WalletCardState extends State<WalletCard> {
balance = widget.walletVM.balance
.totalBalance ??
'0.0';
fiatBalance = '\$ 123.43';
fiatBalance = '\$ 0.00';
// '$symbol ${balanceStore.fiatFullBalance}';
}
@@ -271,6 +260,8 @@ class WalletCardState extends State<WalletCard> {
MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
@@ -284,25 +275,33 @@ class WalletCardState extends State<WalletCard> {
.color),
),
SizedBox(height: 5),
Text(
balance,
style: TextStyle(
fontSize: 28,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
)
Container(
height: 36,
child: Text(
balance,
style: TextStyle(
fontSize: 32,
color: Theme.of(context)
.primaryTextTheme
.title
.color,
fontWeight:
FontWeight.bold),
))
],
),
Text(
fiatBalance,
style: TextStyle(
fontSize: 14,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
fontWeight: FontWeight.w600,
// FIXME
// color: Theme.of(context)
// .primaryTextTheme
// .title
// .color,
color: Color.fromRGBO(
72, 89, 109, 1)),
)
],
);
@@ -320,6 +319,7 @@ class WalletCardState extends State<WalletCard> {
statusText,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme
.caption
@@ -330,6 +330,7 @@ class WalletCardState extends State<WalletCard> {
descriptionText,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme
.title
@@ -351,10 +352,8 @@ class WalletCardState extends State<WalletCard> {
}
Widget backSide(List<Color> colorsSync) {
final rightArrow = Image.asset(
'assets/images/right_arrow.png',
color: Theme.of(context).primaryTextTheme.title.color,
);
final rightArrow = Image.asset('assets/images/right_arrow.png',
color: Theme.of(context).primaryTextTheme.title.color);
var messageBoxHeight = 0.0;
var messageBoxWidth = cardWidth - 10;
@@ -372,7 +371,7 @@ class WalletCardState extends State<WalletCard> {
width: cardWidth,
height: cardHeight,
padding:
EdgeInsets.only(left: 20, right: 20, top: 30, bottom: 30),
EdgeInsets.only(left: 24, right: 24, top: 32, bottom: 32),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
@@ -385,10 +384,10 @@ class WalletCardState extends State<WalletCard> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Container(
height: 90,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
@@ -426,7 +425,8 @@ class WalletCardState extends State<WalletCard> {
child: Text(
widget.walletVM.address,
style: TextStyle(
fontSize: 14,
fontSize: 12,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme
.title
@@ -468,9 +468,10 @@ class WalletCardState extends State<WalletCard> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
S.of(context).accounts_subaddresses,
S.of(context).addresses,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme
.title
+4 -14
View File
@@ -10,6 +10,7 @@ import 'package:cake_wallet/src/screens/base_page.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';
import 'package:cake_wallet/src/widgets/trail_button.dart';
class ExchangePage extends BasePage {
@override
@@ -32,20 +33,9 @@ class ExchangePage extends BasePage {
Widget trailing(BuildContext context) {
final exchangeStore = Provider.of<ExchangeStore>(context);
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).primaryTextTheme.caption.color,
fontWeight: FontWeight.w500,
fontSize: 14),
),
onPressed: () => exchangeStore.reset()),
return TrailButton(
caption: S.of(context).reset,
onPressed: () => exchangeStore.reset()
);
}
@@ -0,0 +1,71 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/monero_account_label_validator.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_state.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.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/base_text_form_field.dart';
class MoneroAccountEditOrCreatePage extends BasePage {
MoneroAccountEditOrCreatePage({@required this.moneroAccountCreationViewModel})
: _formKey = GlobalKey<FormState>(),
_textController = TextEditingController() {
_textController.addListener(
() => moneroAccountCreationViewModel.label = _textController.text);
_textController.text = moneroAccountCreationViewModel.label;
}
final MoneroAccountEditOrCreateViewModel moneroAccountCreationViewModel;
@override
String get title => S.current.account;
final GlobalKey<FormState> _formKey;
final TextEditingController _textController;
@override
Widget body(BuildContext context) =>
Form(
key: _formKey,
child: Container(
color: Theme.of(context).backgroundColor,
padding: EdgeInsets.all(24.0),
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: BaseTextFormField(
controller: _textController,
hintText: S.of(context).account,
validator: MoneroLabelValidator(),
))),
Observer(
builder: (_) =>
LoadingPrimaryButton(
onPressed: () async {
if (!_formKey.currentState.validate()) {
return;
}
await moneroAccountCreationViewModel.save();
Navigator.of(context).pop(_textController.text);
},
text: moneroAccountCreationViewModel.isEdit
? S.of(context).rename
: S.of(context).add,
color: Colors.green,
textColor: Colors.white,
isLoading: moneroAccountCreationViewModel.state
is AccountIsCreating,
isDisabled:
moneroAccountCreationViewModel.label?.isEmpty ?? true,
))
],
),
),
);
}
@@ -1,33 +1,30 @@
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: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/view_model/monero_account_list/monero_account_list_view_model.dart';
import 'package:cake_wallet/src/screens/monero_accounts/widgets/account_tile.dart';
class AccountListPage extends StatefulWidget {
AccountListPage({@required this.accountListStore});
class MoneroAccountListPage extends StatefulWidget {
MoneroAccountListPage({@required this.accountListViewModel});
final AccountListStore accountListStore;
final MoneroAccountListViewModel accountListViewModel;
@override
AccountListPageForm createState() => AccountListPageForm(accountListStore);
MoneroAccountListPageForm createState() =>
MoneroAccountListPageForm(accountListViewModel);
}
class AccountListPageForm extends State<AccountListPage> {
AccountListPageForm(this.accountListStore);
class MoneroAccountListPageForm extends State<MoneroAccountListPage> {
MoneroAccountListPageForm(this.accountListViewModel);
final AccountListStore accountListStore;
final MoneroAccountListViewModel accountListViewModel;
@override
Widget build(BuildContext context) {
final walletStore = Provider.of<WalletStore>(context);
return GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
@@ -35,7 +32,8 @@ class AccountListPageForm extends State<AccountListPage> {
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
decoration: BoxDecoration(
color: PaletteDark.darkNightBlue.withOpacity(0.75)),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
@@ -46,11 +44,10 @@ class AccountListPageForm extends State<AccountListPage> {
S.of(context).choose_account,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: Colors.white
),
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: Colors.white),
),
),
Padding(
@@ -61,52 +58,42 @@ class AccountListPageForm extends State<AccountListPage> {
borderRadius: BorderRadius.all(Radius.circular(14)),
child: Container(
height: 296,
color: Theme.of(context).accentTextTheme.title.backgroundColor,
color: Theme.of(context)
.accentTextTheme
.title
.backgroundColor,
child: Column(
children: <Widget>[
Expanded(
child: Observer(
builder: (_) {
final accounts = accountListStore.accounts;
Expanded(child: Observer(builder: (_) {
final accounts =
widget.accountListViewModel.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 ListView.separated(
separatorBuilder: (context, index) => Divider(
color: Theme.of(context).dividerColor,
height: 1),
itemCount: accounts.length ?? 0,
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();
}
);
return AccountTile(
isCurrent: account.isSelected,
accountName: account.label,
onTap: () {
if (account.isSelected) {
return;
}
);
},
);
}
)
),
widget.accountListViewModel
.select(account);
Navigator.of(context).pop();
});
},
);
})),
GestureDetector(
onTap: () async {
await Navigator.of(context)
.pushNamed(Routes.accountCreation);
accountListStore.updateAccountList();
},
onTap: () async => await Navigator.of(context)
.pushNamed(Routes.accountCreation),
child: Container(
height: 62,
color: Colors.white,
-241
View File
@@ -1,241 +0,0 @@
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/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
String get title => S.current.node_new;
@override
Widget body(BuildContext context) => NewNodePageForm();
}
class NewNodePageForm extends StatefulWidget {
@override
NewNodeFormState createState() => NewNodeFormState();
}
class NewNodeFormState extends State<NewNodePageForm> {
final _formKey = GlobalKey<FormState>();
final _nodeAddressController = TextEditingController();
final _nodePortController = TextEditingController();
final _loginController = TextEditingController();
final _passwordController = TextEditingController();
@override
void dispose() {
_nodeAddressController.dispose();
_nodePortController.dispose();
_loginController.dispose();
_passwordController.dispose();
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);
_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>[
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: () {
_nodeAddressController.text = '';
_nodePortController.text = '';
_loginController.text = '';
_passwordController.text = '';
},
text: S.of(context).reset,
color: Colors.red,
textColor: Colors.white),
)),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 8.0),
child: PrimaryButton(
onPressed: () async {
if (!_formKey.currentState.validate()) {
return;
}
await nodeList.addNode(
address: _nodeAddressController.text,
port: _nodePortController.text,
login: _loginController.text,
password: _passwordController.text);
Navigator.of(context).pop();
},
text: S.of(context).save,
color: Colors.green,
textColor: Colors.white,
isDisabled: nodeList.disabledState,
),
)),
],
)
),
)
);
}
}
@@ -0,0 +1,249 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/node_address_validator.dart';
import 'package:cake_wallet/core/node_port_validator.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';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
class NodeCreateOrEditPage extends BasePage {
NodeCreateOrEditPage(this.nodeCreateOrEditViewModel)
: _formKey = GlobalKey<FormState>(),
_addressController = TextEditingController(),
_portController = TextEditingController(),
_loginController = TextEditingController(),
_passwordController = TextEditingController() {
reaction((_) => nodeCreateOrEditViewModel.address, (String address) {
if (address != _addressController.text) {
_addressController.text = address;
}
});
reaction((_) => nodeCreateOrEditViewModel.port, (String port) {
if (port != _portController.text) {
_portController.text = port;
}
});
if (nodeCreateOrEditViewModel.hasAuthCredentials) {
reaction((_) => nodeCreateOrEditViewModel.login, (String login) {
if (login != _loginController.text) {
_loginController.text = login;
}
});
reaction((_) => nodeCreateOrEditViewModel.password, (String password) {
if (password != _passwordController.text) {
_passwordController.text = password;
}
});
}
_addressController.addListener(
() => nodeCreateOrEditViewModel.address = _addressController.text);
_portController.addListener(
() => nodeCreateOrEditViewModel.port = _portController.text);
_loginController.addListener(
() => nodeCreateOrEditViewModel.login = _loginController.text);
_passwordController.addListener(
() => nodeCreateOrEditViewModel.password = _passwordController.text);
}
final GlobalKey<FormState> _formKey;
final TextEditingController _addressController;
final TextEditingController _portController;
final TextEditingController _loginController;
final TextEditingController _passwordController;
@override
String get title => S.current.node_new;
final NodeCreateOrEditViewModel nodeCreateOrEditViewModel;
@override
Widget body(BuildContext context) {
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>[
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: _addressController,
validator: NodeAddressValidator(),
),
)
],
),
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: _portController,
validator: NodePortValidator(),
),
)
],
),
SizedBox(height: 10.0),
if (nodeCreateOrEditViewModel.hasAuthCredentials) ...[
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: () => nodeCreateOrEditViewModel.reset(),
text: S.of(context).reset,
color: Colors.red,
textColor: Colors.white),
)),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 8.0),
child: PrimaryButton(
onPressed: () async {
if (!_formKey.currentState.validate()) {
return;
}
await nodeCreateOrEditViewModel.save();
Navigator.of(context).pop();
},
text: S.of(context).save,
color: Colors.green,
textColor: Colors.white,
isDisabled: !nodeCreateOrEditViewModel.isReady,
),
)),
],
)),
));
}
}
+85 -170
View File
@@ -1,35 +1,30 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.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/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/standard_list.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
class NodeListPage extends BasePage {
NodeListPage();
NodeListPage(this.nodeListViewModel);
@override
String get title => S.current.nodes;
final NodeListViewModel nodeListViewModel;
@override
Widget trailing(context) {
final nodeList = Provider.of<NodeListStore>(context);
final settings = Provider.of<SettingsStore>(context);
return Container(
height: 32,
width: 72,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(16)),
color: Theme.of(context).accentTextTheme.title.backgroundColor
),
borderRadius: BorderRadius.all(Radius.circular(16)),
color: Theme.of(context).accentTextTheme.title.backgroundColor),
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
@@ -38,24 +33,24 @@ class NodeListPage extends BasePage {
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()
);
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 nodeListViewModel.reset();
},
actionRightButton: () => Navigator.of(context).pop());
});
},
child: Text(
S.of(context).reset,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 10.0,
fontSize: 14.0,
fontWeight: FontWeight.w600,
color: Colors.blue),
)),
),
@@ -63,157 +58,77 @@ class NodeListPage extends BasePage {
}
@override
Widget body(context) => NodeListPageBody();
}
class NodeListPageBody extends StatefulWidget {
@override
NodeListPageBodyState createState() => NodeListPageBodyState();
}
class NodeListPageBodyState extends State<NodeListPageBody> {
@override
Widget build(BuildContext context) {
final nodeList = Provider.of<NodeListStore>(context);
final settings = Provider.of<SettingsStore>(context);
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;
Widget body(context) {
return Container(
height: double.infinity,
padding: EdgeInsets.only(top: 12),
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
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];
padding: EdgeInsets.only(top: 10),
child: Observer(
builder: (_) => SectionStandardList(
sectionCount: 2,
context: context,
itemBuilder: (_, sectionIndex, index) {
if (sectionIndex == 0) {
return NodeHeaderListRow(
title: S.of(context).add_new_node,
onTap: (_) async =>
await Navigator.of(context).pushNamed(Routes.newNode));
}
final isDrawTop = index == 0 ? true : false;
final isDrawBottom = index == nodeList.nodes.length - 1 ? true : false;
final node = nodeListViewModel.nodes[index];
final isSelected = index == 1; // FIXME: hardcoded value.
final nodeListRow = NodeListRow(
title: node.uri,
isSelected: isSelected,
isAlive: node.requestNode(),
onTap: (_) {});
return Observer(
builder: (_) {
final isCurrent = settings.node == null
? false
: node.key == settings.node.key;
final dismissibleRow = 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 =>
nodeListViewModel.delete(node),
direction: DismissDirection.endToStart,
background: Container(
padding: EdgeInsets.only(right: 10.0),
alignment: AlignmentDirectional.centerEnd,
color: Palette.red,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
const Icon(
CupertinoIcons.delete,
color: Colors.white,
),
Text(
S.of(context).delete,
style: TextStyle(color: Colors.white),
)
],
)),
child: nodeListRow);
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);
},
actionRightButton: () => Navigator.of(context).pop()
);
});
}
},
isDrawTop: isDrawTop,
isDrawBottom: isDrawBottom);
return isSelected ? nodeListRow : dismissibleRow;
},
itemCounter: (int sectionIndex) {
if (sectionIndex == 0) {
return 1;
}
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);
},
);
})
),
)
)
],
return nodeListViewModel.nodes.length;
}),
),
);
}
@@ -2,16 +2,17 @@ import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
class NodeIndicator extends StatelessWidget {
NodeIndicator({this.color = Palette.red});
NodeIndicator({this.isLive = false});
final Color color;
final bool isLive;
@override
Widget build(BuildContext context) {
return Container(
width: 8.0,
height: 8.0,
decoration: BoxDecoration(shape: BoxShape.circle, color: color),
decoration: BoxDecoration(
shape: BoxShape.circle, color: isLive ? Palette.green : Palette.red),
);
}
}
@@ -1,62 +1,43 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/nodes/widgets/node_indicator.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:flutter/cupertino.dart';
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});
class NodeListRow extends StandardListRow {
NodeListRow(
{@required String title,
@required void Function(BuildContext context) onTap,
@required bool isSelected,
@required this.isAlive})
: super(title: title, onTap: onTap, isSelected: isSelected);
final String title;
final Widget trailing;
final Color color;
final Color textColor;
final VoidCallback onTap;
final bool isDrawTop;
final bool isDrawBottom;
final Future<bool> isAlive;
@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(),
],
);
Widget buildTrailing(BuildContext context) {
return FutureBuilder(
future: isAlive,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.done:
return NodeIndicator(isLive: snapshot.data as bool);
default:
return NodeIndicator();
}
});
}
}
class NodeHeaderListRow extends StandardListRow {
NodeHeaderListRow({@required String title, @required void Function(BuildContext context) onTap})
: super(title: title, onTap: onTap, isSelected: false);
@override
Widget buildTrailing(BuildContext context) {
return Icon(Icons.add,
color: Theme.of(context).primaryTextTheme.title.color, size: 24.0);
}
}
+46 -10
View File
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/biometric_auth.dart';
abstract class PinCodeWidget extends StatefulWidget {
PinCodeWidget({Key key, this.onPinCodeEntered, this.hasLengthSwitcher})
@@ -29,6 +30,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
static const sixPinLength = 6;
static const fourPinLength = 4;
final _gridViewKey = GlobalKey();
final _key = GlobalKey<ScaffoldState>();
int pinLength = defaultPinLength;
List<int> pin = List<int>.filled(defaultPinLength, null);
@@ -83,9 +85,12 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
}
@override
Widget build(BuildContext context) => Scaffold(body: body(context));
Widget build(BuildContext context) =>
Scaffold(key: _key, body: body(context));
Widget body(BuildContext context) {
final settingsStore = Provider.of<SettingsStore>(context);
final deleteIconImage = Image.asset(
'assets/images/delete_icon.png',
color: Theme.of(context).primaryTextTheme.title.color,
@@ -95,8 +100,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
color: Theme.of(context).primaryTextTheme.title.color,
);
return SafeArea(
child: Container(
return Container(
color: Theme.of(context).backgroundColor,
padding: EdgeInsets.only(left: 40.0, right: 40.0, bottom: 40.0),
child: Column(children: <Widget>[
@@ -105,8 +109,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color
)),
color: Theme.of(context).primaryTextTheme.title.color)),
Spacer(flex: 3),
Container(
width: 180,
@@ -138,7 +141,9 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
},
child: Text(
_changePinLengthText(),
style: TextStyle(fontSize: 14.0, color: Theme.of(context).primaryTextTheme.caption.color),
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.caption.color),
))
],
Spacer(flex: 1),
@@ -161,10 +166,38 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
margin: EdgeInsets.only(
left: marginLeft, right: marginRight),
child: FlatButton(
onPressed: () {},
onPressed: (widget.hasLengthSwitcher ||
!settingsStore
.allowBiometricalAuthentication)
? null
: () {
// FIXME
// if (authStore != null) {
// WidgetsBinding.instance.addPostFrameCallback((_) {
// final biometricAuth = BiometricAuth();
// biometricAuth.isAuthenticated().then(
// (isAuth) {
// if (isAuth) {
// authStore.biometricAuth();
// _key.currentState.showSnackBar(
// SnackBar(
// content: Text(S.of(context).authenticated),
// backgroundColor: Colors.green,
// ),
// );
// }
// }
// );
// });
// }
},
color: Theme.of(context).backgroundColor,
shape: CircleBorder(),
child: faceImage,
child: (widget.hasLengthSwitcher ||
!settingsStore
.allowBiometricalAuthentication)
? Offstage()
: faceImage,
),
);
} else if (index == 10) {
@@ -195,14 +228,17 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
style: TextStyle(
fontSize: 30.0,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color)),
color: Theme.of(context)
.primaryTextTheme
.title
.color)),
),
);
}),
)
: null))
]),
));
);
}
void _push(int num) {
+20 -38
View File
@@ -1,29 +1,22 @@
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:flutter/material.dart';
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/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/di.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/monero_accounts/monero_account_list_page.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';
import 'package:cake_wallet/core/amount_validator.dart';
import 'package:cake_wallet/src/screens/receive/widgets/address_cell.dart';
import 'package:cake_wallet/view_model/address_list/account_list_header.dart';
import 'package:cake_wallet/view_model/address_list/address_list_header.dart';
import 'package:cake_wallet/view_model/address_list/address_list_item.dart';
import 'package:cake_wallet/view_model/address_list/address_list_view_model.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
class ReceivePage extends BasePage {
ReceivePage({this.addressListViewModel})
@@ -33,7 +26,7 @@ class ReceivePage extends BasePage {
_formKey.currentState.validate() ? amountController.text : '');
}
final AddressListViewModel addressListViewModel;
final WalletAddressListViewModel addressListViewModel;
final TextEditingController amountController;
final GlobalKey<FormState> _formKey;
@@ -83,11 +76,6 @@ class ReceivePage extends BasePage {
);
}
@override
Widget build(BuildContext context) {
return super.build(context);
}
@override
Widget body(BuildContext context) {
final copyImage = Image.asset('assets/images/copy_content.png',
@@ -186,7 +174,7 @@ class ReceivePage extends BasePage {
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme
.title
@@ -213,17 +201,11 @@ class ReceivePage extends BasePage {
final item = addressListViewModel.items[index];
Widget cell = Container();
if (item is AccountListHeader) {
if (item is WalletAccountListHeader) {
cell = HeaderTile(
onTap: () async {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
// return AccountListPage(
// accountListStore:
// accountListStore);
});
},
onTap: () async => await showDialog<void>(
context: context,
builder: (_) => getIt.get<MoneroAccountListPage>()),
title: addressListViewModel.accountLabel,
icon: Icon(
Icons.arrow_forward_ios,
@@ -233,11 +215,11 @@ class ReceivePage extends BasePage {
));
}
if (item is AddressListHeader) {
if (item is WalletAddressListHeader) {
cell = HeaderTile(
onTap: () => Navigator.of(context)
.pushNamed(Routes.newSubaddress),
title: S.of(context).subaddresses,
title: S.of(context).addresses,
icon: Icon(
Icons.add,
size: 20,
@@ -246,15 +228,15 @@ class ReceivePage extends BasePage {
));
}
if (item is AddressListItem) {
if (item is WalletAddressListItem) {
cell = Observer(
builder: (_) => AddressCell.fromItem(item,
isCurrent: item.address ==
addressListViewModel.address.address,
onTap: (_) =>
addressListViewModel.address = item,
onEdit: () => Navigator.of(context)
.pushNamed(Routes.newSubaddress, arguments: item)));
onTap: (_) => addressListViewModel.address = item,
onEdit: () => Navigator.of(context).pushNamed(
Routes.newSubaddress,
arguments: item)));
}
return index != 0
@@ -1,10 +1,10 @@
import 'package:flutter/material.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/view_model/address_list/address_list_item.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
class AddressCell extends StatelessWidget {
factory AddressCell.fromItem(AddressListItem item,
factory AddressCell.fromItem(WalletAddressListItem item,
{@required bool isCurrent,
Function(String) onTap,
Function() onEdit}) =>
@@ -47,7 +47,7 @@ class AddressCell extends StatelessWidget {
name ?? address,
style: TextStyle(
fontSize: name?.isNotEmpty ?? false ? 18 : 10,
fontWeight: FontWeight.bold,
fontWeight: FontWeight.w600,
color: isCurrent ? currentTextColor : notCurrentTextColor,
),
),
@@ -32,7 +32,7 @@ class HeaderTile extends StatelessWidget {
title,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
fontWeight: FontWeight.w500,
color: Theme.of(context).primaryTextTheme.title.color
),
),
+2 -3
View File
@@ -20,7 +20,6 @@ import 'package:cake_wallet/src/domain/services/wallet_service.dart';
import 'package:cake_wallet/src/domain/exchange/trade.dart';
import 'package:cake_wallet/src/domain/monero/transaction_description.dart';
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/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
@@ -104,13 +103,13 @@ class RootState extends State<Root> with WidgetsBindingObserver {
return Observer(builder: (_) {
final state = widget.authenticationStore.state;
print(state);
if (state == AuthenticationState.denied) {
return createWelcomePage();
}
if (state == AuthenticationState.installed) {
return getIt.get<AuthPage>();
return getIt.get<AuthPage>(instanceName: 'login');
}
if (state == AuthenticationState.allowed) {
@@ -1,14 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/src/domain/services/wallet_service.dart';
import 'package:cake_wallet/src/screens/seed/seed_page.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/src/stores/wallet_seed/wallet_seed_store.dart';
Widget createSeedPage(
{@required SettingsStore settingsStore,
@required WalletService walletService,
@required void Function() callback}) =>
Provider(
create: (_) => WalletSeedStore(walletService: walletService),
child: SeedPage(onCloseCallback: callback));
-161
View File
@@ -1,161 +0,0 @@
import 'package:provider/provider.dart';
import 'package:esys_flutter_share/esys_flutter_share.dart';
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/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/crypto_lock.png');
@override
String get title => S.current.seed_title;
final VoidCallback onCloseCallback;
@override
void onClose(BuildContext context) =>
onCloseCallback != null ? onCloseCallback() : Navigator.of(context).pop();
@override
Widget leading(BuildContext context) {
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(
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)
),
)
)
],
),
),
),
);
}
}
+154
View File
@@ -0,0 +1,154 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:esys_flutter_share/esys_flutter_share.dart';
import 'package:flutter_mobx/flutter_mobx.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/themes.dart';
import 'package:cake_wallet/theme_changer.dart';
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
class WalletSeedPage extends BasePage {
WalletSeedPage(this.walletSeedViewModel, {this.onCloseCallback});
static final imageLight = Image.asset('assets/images/crypto_lock_light.png');
static final imageDark = Image.asset('assets/images/crypto_lock.png');
@override
String get title => S.current.seed_title;
final VoidCallback onCloseCallback;
final WalletSeedViewModel walletSeedViewModel;
@override
void onClose(BuildContext context) =>
onCloseCallback != null ? onCloseCallback() : Navigator.of(context).pop();
@override
Widget leading(BuildContext context) =>
onCloseCallback != null ? Offstage() : super.leading(context);
@override
Widget trailing(BuildContext context) {
return onCloseCallback != null
? GestureDetector(
onTap: () => onClose(context),
child: Container(
width: 100,
height: 42,
alignment: Alignment.center,
margin: EdgeInsets.only(left: 10),
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: 14,
fontWeight: FontWeight.w600,
color: Colors.blue),
),
),
)
: Offstage();
}
@override
Widget body(BuildContext context) {
final _themeChanger = Provider.of<ThemeChanger>(context);
final image =
_themeChanger.getTheme() == Themes.darkTheme ? imageDark : imageLight;
return Container(
padding: EdgeInsets.all(24),
child: Column(
children: <Widget>[
Flexible(
flex: 2,
child: AspectRatio(
aspectRatio: 1,
child: FittedBox(child: image, fit: BoxFit.fill))),
Flexible(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 33),
child: Observer(builder: (_) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
walletSeedViewModel.name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: Text(
walletSeedViewModel.seed,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
color: Theme.of(context)
.primaryTextTheme
.caption
.color),
),
)
],
);
}),
),
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,
walletSeedViewModel.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: walletSeedViewModel.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)),
))
],
)
],
))
],
));
}
}
+346 -341
View File
@@ -1,5 +1,8 @@
import 'package:cake_wallet/core/address_validator.dart';
import 'package:cake_wallet/core/amount_validator.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/view_model/send_view_model.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -14,7 +17,8 @@ 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/wallet/wallet_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/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/calculate_estimated_fee.dart';
@@ -29,8 +33,13 @@ 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';
import 'package:cake_wallet/src/widgets/trail_button.dart';
class SendPage extends BasePage {
SendPage({@required this.sendViewModel});
final SendViewModel sendViewModel;
@override
String get title => S.current.send_title;
@@ -45,35 +54,20 @@ class SendPage extends BasePage {
@override
Widget trailing(context) {
final sendStore = Provider.of<SendStore>(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),
)),
),
);
return TrailButton(caption: S.of(context).clear, onPressed: () => null);
}
@override
Widget body(BuildContext context) => SendForm();
Widget body(BuildContext context) => SendForm(sendViewModel: sendViewModel);
}
class SendForm extends StatefulWidget {
SendForm({this.sendViewModel});
final SendViewModel sendViewModel;
@override
State<StatefulWidget> createState() => SendFormState();
}
@@ -92,7 +86,7 @@ class SendFormState extends State<SendForm> {
@override
void initState() {
_focusNode.addListener(() {
if (!_focusNode.hasFocus &&_addressController.text.isNotEmpty) {
if (!_focusNode.hasFocus && _addressController.text.isNotEmpty) {
getOpenaliasRecord(context);
}
});
@@ -102,7 +96,8 @@ class SendFormState extends State<SendForm> {
Future<void> getOpenaliasRecord(BuildContext context) async {
final sendStore = Provider.of<SendStore>(context);
final isOpenalias = await sendStore.isOpenaliasRecord(_addressController.text);
final isOpenalias =
await sendStore.isOpenaliasRecord(_addressController.text);
if (isOpenalias) {
_addressController.text = sendStore.recordAddress;
@@ -111,24 +106,24 @@ class SendFormState extends State<SendForm> {
context: context,
builder: (BuildContext context) {
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()
);
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());
});
}
}
@override
Widget build(BuildContext context) {
final settingsStore = Provider.of<SettingsStore>(context);
final sendStore = Provider.of<SendStore>(context);
sendStore.settingsStore = settingsStore;
final balanceStore = Provider.of<BalanceStore>(context);
final walletStore = Provider.of<WalletStore>(context);
final syncStore = Provider.of<SyncStore>(context);
final sendTemplateStore = Provider.of<SendTemplateStore>(context);
// final settingsStore = Provider.of<SettingsStore>(context);
// final sendStore = Provider.of<SendStore>(context);
// sendStore.settingsStore = settingsStore;
// 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);
@@ -166,110 +161,116 @@ class SendFormState extends State<SendForm> {
AddressTextFieldOption.addressBook
],
buttonColor: Theme.of(context).accentTextTheme.title.color,
validator: (value) {
sendStore.validateAddress(value,
cryptoCurrency: CryptoCurrency.xmr);
return sendStore.errorMessage;
},
validator: widget.sendViewModel.addressValidator,
),
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
)
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.w500,
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(
' / ' + widget.sendViewModel.balance,
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: () => null,
// widget.sendViewModel,
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)),
),
),
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;
}),
);
}
),
),
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: widget.sendViewModel.amountValidator),
);
}),
Padding(
padding: const EdgeInsets.only(top: 20),
child: TextFormField(
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color),
color:
Theme.of(context).primaryTextTheme.title.color),
controller: _fiatAmountController,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: true),
@@ -281,16 +282,22 @@ class SendFormState extends State<SendForm> {
prefixIcon: Padding(
padding: EdgeInsets.only(top: 12),
child: Text(
'${settingsStore.fiatCurrency.toString()}:',
'${widget.sendViewModel.fiat.toString()}:',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme
.title
.color,
)),
),
hintStyle: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.caption.color),
color: Theme.of(context)
.primaryTextTheme
.caption
.color),
hintText: '0.00',
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
@@ -310,14 +317,20 @@ class SendFormState extends State<SendForm> {
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color,
color: Theme.of(context)
.primaryTextTheme
.title
.color,
)),
Text(
'${calculateEstimatedFee(priority: settingsStore.transactionPriority)} XMR',
'${widget.sendViewModel.estimatedFee} ${widget.sendViewModel.currency.toString()}',
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color,
color: Theme.of(context)
.primaryTextTheme
.title
.color,
))
],
),
@@ -325,143 +338,140 @@ class SendFormState extends State<SendForm> {
]),
),
),
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);
}
);
}
);
}
),
)
// 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());
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();
sendStore.createTransaction(
address: _addressController.text,
paymentId: '');
});
},
actionRightButton: () =>
Navigator.of(context).pop()
);
});
}
}
: null,
onPressed: () => null,
// syncStore.status is SyncedSyncStatus
// ? () async {
// // Hack. Don't ask me.
// FocusScope.of(context).requestFocus(FocusNode());
//
// 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();
//
// sendStore.createTransaction(
// address: _addressController.text,
// paymentId: '');
// });
// },
// actionRightButton: () =>
// Navigator.of(context).pop());
// });
// }
// }
// : null,
text: S.of(context).send,
color: Colors.blue,
textColor: Colors.white,
isLoading: sendStore.state is CreatingTransaction ||
sendStore.state is TransactionCommiting,
isDisabled: !(syncStore.status is SyncedSyncStatus),
);
isLoading: widget.sendViewModel.state is TransactionIsCreating ||
widget.sendViewModel.state is TransactionCommitting,
isDisabled:
false // FIXME !(syncStore.status is SyncedSyncStatus),
);
}),
),
);
@@ -472,93 +482,88 @@ class SendFormState extends State<SendForm> {
return;
}
final sendStore = Provider.of<SendStore>(context);
// reaction((_) => widget.sendViewModel.fiatAmount, (String amount) {
// if (amount != _fiatAmountController.text) {
// _fiatAmountController.text = amount;
// }
// });
//
// reaction((_) => widget.sendViewModel.cryptoAmount, (String amount) {
// if (amount != _cryptoAmountController.text) {
// _cryptoAmountController.text = amount;
// }
// });
//
// reaction((_) => widget.sendViewModel.address, (String address) {
// if (address != _addressController.text) {
// _addressController.text = address;
// }
// });
//
// _addressController.addListener(() {
// final address = _addressController.text;
//
// if (widget.sendViewModel.address != address) {
// widget.sendViewModel.changeAddress(address);
// }
// });
reaction((_) => sendStore.fiatAmount, (String amount) {
if (amount != _fiatAmountController.text) {
_fiatAmountController.text = amount;
}
});
// _fiatAmountController.addListener(() {
// final fiatAmount = _fiatAmountController.text;
//
// if (sendStore.fiatAmount != fiatAmount) {
// sendStore.changeFiatAmount(fiatAmount);
// }
// });
reaction((_) => sendStore.cryptoAmount, (String amount) {
if (amount != _cryptoAmountController.text) {
_cryptoAmountController.text = amount;
}
});
// _cryptoAmountController.addListener(() {
// final cryptoAmount = _cryptoAmountController.text;
//
// if (sendStore.cryptoAmount != cryptoAmount) {
// sendStore.changeCryptoAmount(cryptoAmount);
// }
// });
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;
if (sendStore.fiatAmount != fiatAmount) {
sendStore.changeFiatAmount(fiatAmount);
}
});
_cryptoAmountController.addListener(() {
final cryptoAmount = _cryptoAmountController.text;
if (sendStore.cryptoAmount != cryptoAmount) {
sendStore.changeCryptoAmount(cryptoAmount);
}
});
reaction((_) => sendStore.state, (SendingState state) {
reaction((_) => widget.sendViewModel.state, (SendViewModelState state) {
if (state is SendingFailed) {
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()
);
alertTitle: S.of(context).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
});
}
if (state is TransactionCreatedSuccessfully) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog<void>(
context: context,
builder: (BuildContext context) {
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()
);
});
});
// WidgetsBinding.instance.addPostFrameCallback((_) {
// showDialog<void>(
// context: context,
// builder: (BuildContext context) {
// 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());
// });
// });
}
if (state is TransactionCommitted) {
@@ -55,7 +55,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
color: Theme.of(context).primaryTextTheme.title.color,
decoration: TextDecoration.none,
),
),
@@ -64,7 +64,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
color: Theme.of(context).primaryTextTheme.title.color,
decoration: TextDecoration.none,
),
)
@@ -79,7 +79,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
color: Theme.of(context).primaryTextTheme.title.color,
decoration: TextDecoration.none,
),
),
@@ -88,7 +88,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Colors.white,
color: Theme.of(context).primaryTextTheme.title.color,
decoration: TextDecoration.none,
),
)
+58 -424
View File
@@ -1,438 +1,72 @@
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/routes.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/fiat_currency.dart';
import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/src/stores/action_list/action_list_display_mode.dart';
import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
import 'package:cake_wallet/view_model/settings/link_list_item.dart';
import 'package:cake_wallet/view_model/settings/picker_list_item.dart';
import 'package:cake_wallet/view_model/settings/regular_list_item.dart';
import 'package:cake_wallet/view_model/settings/switcher_list_item.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_link_provider_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
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/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';
import 'package:cake_wallet/src/screens/settings/widgets/settings_link_list_row.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switch_list_row.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_text_list_row.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_raw_widget_list_row.dart';
class SettingsPage extends BasePage {
SettingsPage(this.settingsViewModel);
final SettingsViewModel settingsViewModel;
@override
String get title => S.current.settings_title;
@override
Widget body(BuildContext context) {
return SettingsForm();
}
}
class SettingsForm extends StatefulWidget {
@override
SettingsFormState createState() => SettingsFormState();
}
class SettingsFormState extends State<SettingsForm> {
final _telegramImage = Image.asset('assets/images/Telegram.png');
final _twitterImage = Image.asset('assets/images/Twitter.png');
final _changeNowImage = Image.asset('assets/images/change_now.png');
final _xmrBtcImage = Image.asset('assets/images/xmr_btc.png');
final _morphImage = Image.asset('assets/images/morph_icon.png');
final _emailUrl = 'mailto:support@cakewallet.com';
final _telegramUrl = 'https:t.me/cakewallet_bot';
final _twitterUrl = 'https:twitter.com/CakewalletXMR';
final _changeNowUrl = 'mailto:support@changenow.io';
final _xmrToUrl = 'mailto:support@xmr.to';
final _morphUrl = 'mailto:support@morphtoken.com';
final _items = List<SettingsItem>();
void _launchUrl(String url) async {
if (await canLaunch(url)) await launch(url);
}
void _setSettingsList() {
final settingsStore = Provider.of<SettingsStore>(context);
settingsStore.setItemHeaders();
_items.addAll([
SettingsItem(
onTaped: () => _setBalance(context),
title: ItemHeaders.displayBalanceAs,
widget: Observer(
builder: (_) => Text(
settingsStore.balanceDisplayMode.toString(),
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.caption.color),
)),
attribute: Attributes.widget),
SettingsItem(
onTaped: () => _setCurrency(context),
title: ItemHeaders.currency,
widget: Observer(
builder: (_) => Text(
settingsStore.fiatCurrency.toString(),
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.caption.color),
)),
attribute: Attributes.widget),
SettingsItem(
onTaped: () => _setTransactionPriority(context),
title: ItemHeaders.feePriority,
widget: Observer(
builder: (_) => Text(
settingsStore.transactionPriority.toString(),
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.caption.color),
)),
attribute: Attributes.widget),
SettingsItem(
title: ItemHeaders.saveRecipientAddress,
attribute: Attributes.switcher),
SettingsItem(title: '', attribute: Attributes.header),
SettingsItem(
onTaped: () {
Navigator.of(context).pushNamed(Routes.auth,
arguments: (bool isAuthenticatedSuccessfully,
AuthPageState auth) =>
isAuthenticatedSuccessfully
? Navigator.of(context).popAndPushNamed(Routes.setupPin,
arguments:
(BuildContext setupPinContext, String _) =>
Navigator.of(context).pop())
: null);
},
title: ItemHeaders.changePIN,
attribute: Attributes.arrow),
SettingsItem(
onTaped: () => Navigator.pushNamed(context, Routes.changeLanguage),
title: ItemHeaders.changeLanguage,
attribute: Attributes.arrow),
SettingsItem(
title: ItemHeaders.allowBiometricalAuthentication,
attribute: Attributes.switcher),
SettingsItem(title: ItemHeaders.darkMode, attribute: Attributes.switcher),
SettingsItem(
widgetBuilder: (context) {
return PopupMenuButton<ActionListDisplayMode>(
itemBuilder: (context) => [
PopupMenuItem(
value: ActionListDisplayMode.transactions,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(S
.of(context)
.settings_transactions,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.title.color
),
),
Checkbox(
value: settingsStore
.actionlistDisplayMode
.contains(ActionListDisplayMode
.transactions),
onChanged: (value) => settingsStore
.toggleTransactionsDisplay(),
)
]))),
PopupMenuItem(
value: ActionListDisplayMode.trades,
child: Observer(
builder: (_) => Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
S.of(context).settings_trades,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.title.color
),
),
Checkbox(
value: settingsStore
.actionlistDisplayMode
.contains(
ActionListDisplayMode.trades),
onChanged: (value) => settingsStore
.toggleTradesDisplay(),
)
])))
],
child: Container(
height: 56,
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: 14,
color: Theme.of(context).primaryTextTheme.title.color)),
Observer(builder: (_) {
var title = '';
if (settingsStore.actionlistDisplayMode.length ==
ActionListDisplayMode.values.length) {
title = S.of(context).settings_all;
}
if (title.isEmpty &&
settingsStore.actionlistDisplayMode
.contains(ActionListDisplayMode.trades)) {
title = S.of(context).settings_only_trades;
}
if (title.isEmpty &&
settingsStore.actionlistDisplayMode.contains(
ActionListDisplayMode.transactions)) {
title = S.of(context).settings_only_transactions;
}
if (title.isEmpty) {
title = S.of(context).settings_none;
}
return Text(title,
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.caption.color));
})
]),
));
},
attribute: Attributes.rawWidget),
SettingsItem(title: '', attribute: Attributes.header),
SettingsItem(
onTaped: () => _launchUrl(_emailUrl),
title: 'Email',
link: 'support@cakewallet.com',
image: null,
attribute: Attributes.link),
SettingsItem(
onTaped: () => _launchUrl(_telegramUrl),
title: 'Telegram',
link: 'Cake_Wallet',
image: _telegramImage,
attribute: Attributes.link),
SettingsItem(
onTaped: () => _launchUrl(_twitterUrl),
title: 'Twitter',
link: '@CakeWalletXMR',
image: _twitterImage,
attribute: Attributes.link),
SettingsItem(
onTaped: () => _launchUrl(_changeNowUrl),
title: 'ChangeNow',
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: () {
Navigator.push(
context,
CupertinoPageRoute<void>(
builder: (BuildContext context) => DisclaimerPage()));
},
title: ItemHeaders.termsAndConditions,
attribute: Attributes.arrow),
SettingsItem(
onTaped: () => Navigator.pushNamed(context, Routes.faq),
title: ItemHeaders.faq,
attribute: Attributes.arrow)
]);
setState(() {});
}
void _afterLayout(dynamic _) => _setSettingsList();
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
}
Widget _getWidget(SettingsItem item) {
switch (item.attribute) {
case Attributes.arrow:
return SettingsArrowListRow(
onTaped: item.onTaped,
title: item.title,
);
case Attributes.header:
return SettingsHeaderListRow(
title: item.title,
);
case Attributes.link:
return SettingsLinktListRow(
onTaped: item.onTaped,
title: item.title,
link: item.link,
image: item.image,
);
case Attributes.switcher:
return SettingsSwitchListRow(
title: item.title,
);
case Attributes.widget:
return SettingsTextListRow(
onTaped: item.onTaped,
title: item.title,
widget: item.widget,
);
case Attributes.rawWidget:
return SettingRawWidgetListRow(widgetBuilder: item.widgetBuilder);
default:
return Offstage();
}
}
@override
Widget build(BuildContext context) {
final settingsStore = Provider.of<SettingsStore>(context);
settingsStore.setItemHeaders();
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: 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 items = BalanceDisplayMode.all;
final selectedItem = items.indexOf(settingsStore.balanceDisplayMode);
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 items = FiatCurrency.all;
final selectedItem = items.indexOf(settingsStore.fiatCurrency);
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 items = TransactionPriority.all;
final selectedItem = items.indexOf(settingsStore.transactionPriority);
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);
return SectionStandardList(
sectionCount: settingsViewModel.sections.length,
itemCounter: (int sectionIndex) {
if (sectionIndex < settingsViewModel.sections.length) {
return settingsViewModel.sections[sectionIndex].length;
}
return 0;
},
itemBuilder: (_, sectionIndex, itemIndex) {
final item = settingsViewModel.sections[sectionIndex][itemIndex];
if (item is PickerListItem) {
return Observer(builder: (_) {
return SettingsPickerCell<dynamic>(
title: item.title,
selectedItem: item.selectedItem(),
items: item.items);
});
}
if (item is SwitcherListItem) {
return Observer(builder: (_) {
return SettingsSwitcherCell(
title: item.title,
value: item.value(),
onValueChange: item.onValueChange);
});
}
if (item is RegularListItem) {
return SettingsCellWithArrow(title: item.title);
}
if (item is LinkListItem) {
return SettingsLinkProviderCell(
title: item.title,
icon: item.icon,
link: item.link,
linkTitle: item.linkTitle);
}
return Container();
});
}
}
@@ -1,33 +0,0 @@
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
class SettingsArrowListRow extends StatelessWidget {
SettingsArrowListRow({@required this.onTaped, this.title});
final VoidCallback onTaped;
final String title;
@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.title.backgroundColor,
child: ListTile(
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
title: Observer(
builder: (_) => Text(
settingsStore.itemHeaders[title],
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.title.color),
)),
trailing: _cakeArrowImage,
onTap: onTaped),
);
}
}
@@ -0,0 +1,12 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
class SettingsCellWithArrow extends StandardListRow {
SettingsCellWithArrow({@required String title})
: super(title: title, isSelected: false);
@override
Widget buildTrailing(BuildContext context) =>
Image.asset('assets/images/select_arrow.png',
color: Theme.of(context).primaryTextTheme.caption.color);
}
@@ -1,43 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:provider/provider.dart';
class SettingsHeaderListRow extends StatelessWidget {
SettingsHeaderListRow({this.title});
final String title;
@override
Widget build(BuildContext context) {
final settingsStore = Provider.of<SettingsStore>(context);
return Column(
children: <Widget>[
SizedBox(
height: 20.0,
),
Container(
padding: EdgeInsets.only(left: 20.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Observer(
builder: (_) => Text(
title.isNotEmpty
? settingsStore.itemHeaders[title]
: '',
style: TextStyle(
fontSize: 15.0,
color: Theme.of(context).primaryTextTheme.caption.color),
))
],
),
),
SizedBox(
height: 14.0,
),
],
);
}
}
@@ -1,42 +0,0 @@
import 'package:flutter/material.dart';
class SettingsLinktListRow extends StatelessWidget {
SettingsLinktListRow(
{@required this.onTaped, this.title, this.link, this.image});
final VoidCallback onTaped;
final String title;
final String link;
final Image image;
@override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: ListTile(
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
image != null ? image : Offstage(),
Container(
padding: image != null ? EdgeInsets.only(left: 10) : null,
child: Text(
title,
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.title.color),
),
)
],
),
trailing: Text(
link,
style: TextStyle(fontSize: 14.0, color: Colors.blue),
),
onTap: onTaped,
),
);
}
}
@@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
class SettingsLinkProviderCell extends StandardListRow {
SettingsLinkProviderCell(
{@required String title,
@required this.icon,
@required this.link,
@required this.linkTitle})
: super(title: title, isSelected: false);
final String icon;
final String link;
final String linkTitle;
@override
Widget buildLeading(BuildContext context) =>
icon != null ? Image.asset(icon) : null;
@override
Widget buildTrailing(BuildContext context) => Text(linkTitle,
style: TextStyle(
fontSize: 14.0, fontWeight: FontWeight.w500, color: Colors.blue));
}
@@ -0,0 +1,38 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/generated/i18n.dart';
class SettingsPickerCell<ItemType> extends StandardListRow {
SettingsPickerCell({@required String title, this.selectedItem, this.items})
: super(
title: title,
isSelected: false,
onTap: (BuildContext context) async {
final selectedAtIndex = items.indexOf(selectedItem);
await showDialog<void>(
context: context,
builder: (_) => Picker(
items: items,
selectedAtIndex: selectedAtIndex,
title: S.current.please_select,
mainAxisAlignment: MainAxisAlignment.center,
onItemSelected: (Object _) {}));
});
final ItemType selectedItem;
final List<ItemType> items;
@override
Widget buildTrailing(BuildContext context) {
return Text(
selectedItem.toString(),
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 14.0,
fontWeight: FontWeight.w500,
color: Theme.of(context).primaryTextTheme.caption.color),
);
}
}
@@ -0,0 +1,9 @@
import 'package:flutter/cupertino.dart';
class SettingsPickerRaw extends StatelessWidget {
@override
Widget build(BuildContext context) {
// TODO: implement build
throw UnimplementedError();
}
}
@@ -1,15 +0,0 @@
import 'package:flutter/material.dart';
class SettingRawWidgetListRow extends StatelessWidget {
SettingRawWidgetListRow({@required this.widgetBuilder});
final WidgetBuilder widgetBuilder;
@override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: widgetBuilder(context) ?? Container(),
);
}
}
@@ -1,76 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/theme_changer.dart';
import 'package:cake_wallet/themes.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/src/widgets/standart_switch.dart';
class SettingsSwitchListRow extends StatelessWidget {
SettingsSwitchListRow({@required this.title});
final String title;
Widget _getSwitch(BuildContext context) {
final settingsStore = Provider.of<SettingsStore>(context);
final _themeChanger = Provider.of<ThemeChanger>(context);
if (settingsStore.itemHeaders[title] ==
S.of(context).settings_save_recipient_address) {
return Observer(
builder: (_) => StandartSwitch(
value: settingsStore.shouldSaveRecipientAddress,
onTaped: () {
final _currentValue = !settingsStore.shouldSaveRecipientAddress;
settingsStore.setSaveRecipientAddress(
shouldSaveRecipientAddress: _currentValue);
}));
}
if (settingsStore.itemHeaders[title] ==
S.of(context).settings_allow_biometrical_authentication) {
return Observer(
builder: (_) => StandartSwitch(
value: settingsStore.allowBiometricalAuthentication,
onTaped: () {
final _currentValue =
!settingsStore.allowBiometricalAuthentication;
settingsStore.setAllowBiometricalAuthentication(
allowBiometricalAuthentication: _currentValue);
}));
}
if (settingsStore.itemHeaders[title] == S.of(context).settings_dark_mode) {
return Observer(
builder: (_) => StandartSwitch(
value: settingsStore.isDarkTheme,
onTaped: () {
final _currentValue = !settingsStore.isDarkTheme;
settingsStore.saveDarkTheme(isDarkTheme: _currentValue);
_themeChanger.setTheme(
_currentValue ? Themes.darkTheme : Themes.lightTheme);
}));
}
return null;
}
@override
Widget build(BuildContext context) {
final settingsStore = Provider.of<SettingsStore>(context);
return Container(
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: ListTile(
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
title: Observer(
builder: (_) => Text(settingsStore.itemHeaders[title],
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryTextTheme.title.color)),
),
trailing: _getSwitch(context)),
);
}
}
@@ -0,0 +1,16 @@
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/src/widgets/standart_switch.dart';
class SettingsSwitcherCell extends StandardListRow {
SettingsSwitcherCell(
{@required String title, @required this.value, this.onValueChange})
: super(title: title, isSelected: false);
final bool value;
final void Function(bool value) onValueChange;
@override
Widget buildTrailing(BuildContext context) =>
StandartSwitch(value: value, onTaped: () => onValueChange(!value));
}
@@ -1,42 +0,0 @@
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
class SettingsTextListRow extends StatelessWidget {
SettingsTextListRow({@required this.onTaped, this.title, this.widget});
final VoidCallback onTaped;
final String title;
final Widget widget;
@override
Widget build(BuildContext context) {
final settingsStore = Provider.of<SettingsStore>(context);
return Container(
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: ListTile(
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Flexible(
child: Observer(
builder: (_) => Text(
settingsStore.itemHeaders[title],
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.title.color),
)),
),
Flexible(
child: widget
)
],
),
onTap: onTaped,
),
);
}
}
@@ -1,80 +0,0 @@
import 'package:provider/provider.dart';
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/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
String get title => S.current.wallet_keys;
@override
Widget body(BuildContext context) {
final walletKeysStore = Provider.of<WalletKeysStore>(context);
return Container(
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Observer(
builder: (_) {
final keysMap = {
S.of(context).view_key_public: walletKeysStore.publicViewKey,
S.of(context).spend_key_private: walletKeysStore.privateSpendKey
};
if (walletKeysStore.privateViewKey.isNotEmpty) {
keysMap[S.of(context).view_key_private] =
walletKeysStore.privateViewKey;
}
if (walletKeysStore.publicSpendKey.isNotEmpty) {
keysMap[S.of(context).spend_key_public] =
walletKeysStore.publicSpendKey;
}
return ListView.separated(
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);
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,
),
);
});
},
));
}
}
@@ -3,8 +3,8 @@ 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/view_model/address_list/address_edit_or_create_view_model.dart';
import 'package:cake_wallet/core/AddressLabelValidator.dart';
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart';
import 'package:cake_wallet/core/address_label_validator.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
@@ -21,7 +21,7 @@ class AddressEditOrCreatePage extends BasePage {
print(addressEditOrCreateViewModel.label);
}
final AddressEditOrCreateViewModel addressEditOrCreateViewModel;
final WalletAddressEditOrCreateViewModel addressEditOrCreateViewModel;
final GlobalKey<FormState> _formKey;
final TextEditingController _labelController;
@@ -1,52 +1,21 @@
import 'package:cake_wallet/src/domain/monero/monero_transaction_info.dart';
import 'package:provider/provider.dart';
import 'package:intl/intl.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
import 'package:cake_wallet/src/domain/monero/monero_transaction_info.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/widgets/standart_list_row.dart';
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.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
String get title => S.current.transaction_details_title;
@override
Widget body(BuildContext context) {
final settingsStore = Provider.of<SettingsStore>(context);
return TransactionDetailsForm(
transactionInfo: transactionInfo, settingsStore: settingsStore);
}
}
class TransactionDetailsForm extends StatefulWidget {
TransactionDetailsForm(
{@required this.transactionInfo, @required this.settingsStore});
final TransactionInfo transactionInfo;
final SettingsStore settingsStore;
@override
TransactionDetailsFormState createState() => TransactionDetailsFormState();
}
class TransactionDetailsFormState extends State<TransactionDetailsForm> {
final _items = List<StandartListItem>();
@override
void initState() {
final _dateFormat = widget.settingsStore.getCurrentDateFormat(
formatUSA: "yyyy.MM.dd, HH:mm", formatDefault: "dd.MM.yyyy, HH:mm");
final tx = widget.transactionInfo;
TransactionDetailsPage({this.transactionInfo}) : _items = [] {
// FIXME
// final _dateFormat = widget.settingsStore.getCurrentDateFormat(
// formatUSA: "yyyy.MM.dd, HH:mm", formatDefault: "dd.MM.yyyy, HH:mm");
final dateFormat = DateFormat('dd.MM.yyyy, HH:mm');
final tx = transactionInfo;
if (tx is MoneroTransactionInfo) {
final items = [
@@ -54,7 +23,31 @@ class TransactionDetailsFormState extends State<TransactionDetailsForm> {
title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(
title: S.current.transaction_details_date,
value: _dateFormat.format(tx.date)),
value: dateFormat.format(tx.date)),
StandartListItem(
title: S.current.transaction_details_height, value: '${tx.height}'),
StandartListItem(
title: S.current.transaction_details_amount,
value: tx.amountFormatted())
];
// FIXME
// if (widget.settingsStore.shouldSaveRecipientAddress &&
// tx.recipientAddress != null) {
// items.add(StandartListItem(
// title: S.current.transaction_details_recipient_address,
// value: tx.recipientAddress));
// }
_items.addAll(items);
}
if (tx is BitcoinTransactionInfo) {
final items = [
StandartListItem(
title: S.current.transaction_details_transaction_id, value: tx.id),
StandartListItem(
title: S.current.transaction_details_date,
value: dateFormat.format(tx.date)),
StandartListItem(
title: S.current.transaction_details_height, value: '${tx.height}'),
StandartListItem(
@@ -62,33 +55,31 @@ class TransactionDetailsFormState extends State<TransactionDetailsForm> {
value: tx.amountFormatted())
];
if (widget.settingsStore.shouldSaveRecipientAddress &&
tx.recipientAddress != null) {
items.add(StandartListItem(
title: S.current.transaction_details_recipient_address,
value: tx.recipientAddress));
}
_items.addAll(items);
}
super.initState();
}
@override
Widget build(BuildContext context) {
String get title => S.current.transaction_details_title;
final TransactionInfo transactionInfo;
final List<StandartListItem> _items;
@override
Widget body(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 20, bottom: 20),
child: ListView.separated(
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,
),
),
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];
@@ -108,8 +99,7 @@ class TransactionDetailsFormState extends State<TransactionDetailsForm> {
),
);
},
child:
StandartListRow(
child: StandartListRow(
title: '${item.title}:',
value: item.value,
isDrawTop: isDrawTop,
@@ -0,0 +1,63 @@
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/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
class WalletKeysPage extends BasePage {
WalletKeysPage(this.walletKeysViewModel);
@override
String get title => S.current.wallet_keys;
final WalletKeysViewModel walletKeysViewModel;
@override
Widget body(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Observer(
builder: (_) {
return ListView.separated(
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: walletKeysViewModel.items.length,
itemBuilder: (BuildContext context, int index) {
final item = walletKeysViewModel.items[index];
return GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(text: item.value));
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
S.of(context).copied_key_to_clipboard(item.title),
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.green,
duration: Duration(seconds: 1),
));
},
child: StandartListRow(
title: item.title + ':',
value: item.value,
),
);
});
},
));
}
}
+143 -132
View File
@@ -1,177 +1,188 @@
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/routes.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.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';
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/screens/wallet_list/widgets/wallet_tile.dart';
class WalletListPage extends BasePage {
WalletListPage({this.walletListViewModel});
final WalletListViewModel walletListViewModel;
@override
Widget body(BuildContext context) => WalletListBody();
Widget body(BuildContext context) =>
WalletListBody(walletListViewModel: walletListViewModel);
}
class WalletListBody extends StatefulWidget {
WalletListBody({this.walletListViewModel});
final WalletListViewModel walletListViewModel;
@override
WalletListBodyState createState() => WalletListBodyState();
}
class WalletListBodyState extends State<WalletListBody> {
final moneroIcon = Image.asset('assets/images/monero.png', height: 24, width: 24);
WalletListStore _walletListStore;
ScrollController scrollController = ScrollController();
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 scrollController = ScrollController();
@override
Widget build(BuildContext context) {
final walletStore = Provider.of<WalletStore>(context);
_walletListStore = Provider.of<WalletListStore>(context);
final newWalletImage = Image.asset('assets/images/new_wallet.png',
height: 12,
width: 12,
color: Palette.oceanBlue);
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;
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: widget.walletListViewModel.wallets.length,
itemBuilder: (__, index) {
final wallet = widget.walletListViewModel.wallets[index];
final screenWidth = MediaQuery.of(context).size.width;
// String shortAddress = '';
final isCurrentWallet =
_walletListStore.isCurrentWallet(wallet);
// if (wallet.isCurrent) {
// shortAddress = wallet.address;
// shortAddress = shortAddress.replaceRange(
// 4, shortAddress.length - 4, '...');
// }
String shortAddress = '';
final walletMenu = WalletMenu(context, widget.walletListViewModel);
final items =
walletMenu.generateItemsForWalletMenu(wallet.isCurrent);
final colors = walletMenu
.generateColorsForWalletMenu(wallet.isCurrent);
final images = walletMenu
.generateImagesForWalletMenu(wallet.isCurrent);
if (isCurrentWallet) {
shortAddress = walletStore.subaddress.address;
shortAddress = shortAddress.replaceRange(4, shortAddress.length - 4, '...');
}
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: _imageFor(type: wallet.type),
walletName: wallet.name,
walletAddress: '', //shortAddress,
isCurrent: wallet.isCurrent),
),
SliverList(
delegate:
SliverChildBuilderDelegate((context, index) {
final item = items[index];
final color = colors[index];
final image = images[index];
final walletMenu = WalletMenu(context);
final items = walletMenu.generateItemsForWalletMenu(isCurrentWallet);
final colors = walletMenu.generateColorsForWalletMenu(isCurrentWallet);
final images = walletMenu.generateImagesForWalletMenu(isCurrentWallet);
final radius = index == 0 ? 12.0 : 0.0;
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
return GestureDetector(
onTap: () {
scrollController.animateTo(0.0,
duration: Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn);
walletMenu.action(
walletMenu.listItems.indexOf(item),
wallet,
wallet.isCurrent);
},
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),
)
],
),
),
),
),
),
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
)
)
],
),
);
}),
),
);
}, 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),
),
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.restoreWalletType),
image: restoreWalletImage,
text: S.of(context).wallet_list_restore_wallet,
color: Theme.of(context).primaryTextTheme.overline.color,
textColor: Theme.of(context).primaryTextTheme.title.color)
])),
)
);
])),
));
}
Image _imageFor({WalletType type}) {
switch (type) {
case WalletType.bitcoin:
return bitcoinIcon;
case WalletType.monero:
return moneroIcon;
default:
return null;
}
}
}
+7 -7
View File
@@ -1,14 +1,16 @@
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/stores/wallet_list/wallet_list_store.dart';
import 'package:cake_wallet/src/domain/common/wallet_description.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
class WalletMenu {
WalletMenu(this.context);
WalletMenu(this.context, this.walletListViewModel);
final WalletListViewModel walletListViewModel;
final BuildContext context;
final List<String> listItems = [
@@ -65,9 +67,7 @@ class WalletMenu {
return images;
}
void action(int index, WalletDescription wallet, bool isCurrentWallet) {
final _walletListStore = Provider.of<WalletListStore>(context);
void action(int index, WalletListItem wallet, bool isCurrentWallet) {
switch (index) {
case 0:
Navigator.of(context).pushNamed(Routes.auth, arguments:
@@ -79,7 +79,7 @@ class WalletMenu {
try {
auth.changeProcessText(
S.of(context).wallet_list_loading_wallet(wallet.name));
await _walletListStore.loadWallet(wallet);
await walletListViewModel.loadWallet(wallet);
auth.close();
Navigator.of(context).pop();
} catch (e) {
@@ -109,7 +109,7 @@ class WalletMenu {
try {
auth.changeProcessText(
S.of(context).wallet_list_removing_wallet(wallet.name));
await _walletListStore.remove(wallet);
// await _walletListStore.remove(wallet);
auth.close();
} catch (e) {
auth.changeProcessText(S
@@ -20,11 +20,10 @@ class WalletTile extends SliverPersistentHeaderDelegate {
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
double opacity = 1 - shrinkOffset / (max - min);
var opacity = 1 - shrinkOffset / (max - min);
opacity = opacity >= 0 ? opacity : 0;
double panelWidth = 12 * opacity;
var panelWidth = 12 * opacity;
panelWidth = panelWidth < 12 ? 0 : 12;
final currentColor = isCurrent
+90 -75
View File
@@ -4,11 +4,14 @@ 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';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/themes.dart';
import 'package:cake_wallet/theme_changer.dart';
class WelcomePage extends BasePage {
static const aspectRatioImage = 1.25;
final welcomeImage = Image.asset('assets/images/welcome.png');
final welcomeImageLight = Image.asset('assets/images/welcome_light.png');
final welcomeImageDark = Image.asset('assets/images/welcome.png');
@override
Widget build(BuildContext context) {
@@ -20,6 +23,10 @@ class WelcomePage extends BasePage {
@override
Widget body(BuildContext context) {
final _themeChanger = Provider.of<ThemeChanger>(context);
final welcomeImage = _themeChanger.getTheme() == Themes.darkTheme
? welcomeImageDark : welcomeImageLight;
final newWalletImage = Image.asset('assets/images/new_wallet.png',
height: 12,
width: 12,
@@ -30,89 +37,97 @@ class WelcomePage extends BasePage {
color: Theme.of(context).primaryTextTheme.title.color);
return Container(
padding: EdgeInsets.only(top: 20),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 20),
content: Column(
children: <Widget>[
AspectRatio(
padding: EdgeInsets.all(24),
child: Column(
children: <Widget>[
Flexible(
flex: 2,
child: 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,
child: FittedBox(child: welcomeImage, fit: BoxFit.fill)
)
),
Flexible(
flex: 3,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 24),
child: Text(
S.of(context).welcome,
style: TextStyle(
fontSize: 18,
color: Theme.of(context).primaryTextTheme.caption.color,
),
textAlign: TextAlign.center,
),
textAlign: TextAlign.center,
),
),
Padding(
padding: EdgeInsets.only(top: 14),
child: Text(
S.of(context).first_wallet_text,
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,
),
),
],
),
Column(
children: <Widget>[
Text(
S.of(context).please_make_selection,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
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),
)
],
)
],
)
],
),
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),
)
]),
),
],
)
);
}
}
@@ -1,111 +1,111 @@
import 'package:mobx/mobx.dart';
import 'package:flutter/foundation.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/contact.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:hive/hive.dart';
part 'address_book_store.g.dart';
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;
@observable
String errorMessage;
Box<Contact> contacts;
@action
Future add({Contact contact}) async => contacts.add(contact);
@action
Future updateContactList() async => contactList = contacts.values.toList();
@action
Future update({Contact contact}) async => contact.save();
@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);
isValid = regExp.hasMatch(value);
errorMessage = isValid ? null : S.current.error_text_contact_name;
}
void validateAddress(String value, {CryptoCurrency cryptoCurrency}) {
// XMR (95, 106), ADA (59, 92, 105), BCH (42), BNB (42), BTC (34, 42), DASH (34), EOS (42),
// ETH (42), LTC (34), NANO (64, 65), TRX (34), USDT (42), XLM (56), XRP (34)
const pattern = '^[0-9a-zA-Z]{95}\$|^[0-9a-zA-Z]{34}\$|^[0-9a-zA-Z]{42}\$|^[0-9a-zA-Z]{56}\$|^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z_]{64}\$|^[0-9a-zA-Z_]{65}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{105}\$|^[0-9a-zA-Z]{106}\$';
final regExp = RegExp(pattern);
isValid = regExp.hasMatch(value);
if (isValid && cryptoCurrency != null) {
switch (cryptoCurrency) {
case CryptoCurrency.xmr:
isValid = (value.length == 95)||(value.length == 106);
break;
case CryptoCurrency.ada:
isValid = (value.length == 59)||(value.length == 92)||(value.length == 105);
break;
case CryptoCurrency.bch:
isValid = (value.length == 42);
break;
case CryptoCurrency.bnb:
isValid = (value.length == 42);
break;
case CryptoCurrency.btc:
isValid = (value.length == 34)||(value.length == 42);
break;
case CryptoCurrency.dash:
isValid = (value.length == 34);
break;
case CryptoCurrency.eos:
isValid = (value.length == 42);
break;
case CryptoCurrency.eth:
isValid = (value.length == 42);
break;
case CryptoCurrency.ltc:
isValid = (value.length == 34);
break;
case CryptoCurrency.nano:
isValid = (value.length == 64)||(value.length == 65);
break;
case CryptoCurrency.trx:
isValid = (value.length == 34);
break;
case CryptoCurrency.usdt:
isValid = (value.length == 42);
break;
case CryptoCurrency.xlm:
isValid = (value.length == 56);
break;
case CryptoCurrency.xrp:
isValid = (value.length == 34);
break;
}
}
errorMessage = isValid ? null : S.current.error_text_address;
}
}
//import 'package:mobx/mobx.dart';
//import 'package:flutter/foundation.dart';
//import 'package:cake_wallet/generated/i18n.dart';
//import 'package:cake_wallet/src/domain/common/contact.dart';
//import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
//import 'package:hive/hive.dart';
//
//part 'address_book_store.g.dart';
//
//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;
//
// @observable
// String errorMessage;
//
// Box<Contact> contacts;
//
// @action
// Future add({Contact contact}) async => contacts.add(contact);
//
// @action
// Future updateContactList() async => contactList = contacts.values.toList();
//
// @action
// Future update({Contact contact}) async => contact.save();
//
// @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);
// isValid = regExp.hasMatch(value);
// errorMessage = isValid ? null : S.current.error_text_contact_name;
// }
//
// void validateAddress(String value, {CryptoCurrency cryptoCurrency}) {
// // XMR (95, 106), ADA (59, 92, 105), BCH (42), BNB (42), BTC (34, 42), DASH (34), EOS (42),
// // ETH (42), LTC (34), NANO (64, 65), TRX (34), USDT (42), XLM (56), XRP (34)
// const pattern = '^[0-9a-zA-Z]{95}\$|^[0-9a-zA-Z]{34}\$|^[0-9a-zA-Z]{42}\$|^[0-9a-zA-Z]{56}\$|^[0-9a-zA-Z]{59}\$|^[0-9a-zA-Z_]{64}\$|^[0-9a-zA-Z_]{65}\$|^[0-9a-zA-Z]{92}\$|^[0-9a-zA-Z]{105}\$|^[0-9a-zA-Z]{106}\$';
// final regExp = RegExp(pattern);
// isValid = regExp.hasMatch(value);
// if (isValid && cryptoCurrency != null) {
// switch (cryptoCurrency) {
// case CryptoCurrency.xmr:
// isValid = (value.length == 95)||(value.length == 106);
// break;
// case CryptoCurrency.ada:
// isValid = (value.length == 59)||(value.length == 92)||(value.length == 105);
// break;
// case CryptoCurrency.bch:
// isValid = (value.length == 42);
// break;
// case CryptoCurrency.bnb:
// isValid = (value.length == 42);
// break;
// case CryptoCurrency.btc:
// isValid = (value.length == 34)||(value.length == 42);
// break;
// case CryptoCurrency.dash:
// isValid = (value.length == 34);
// break;
// case CryptoCurrency.eos:
// isValid = (value.length == 42);
// break;
// case CryptoCurrency.eth:
// isValid = (value.length == 42);
// break;
// case CryptoCurrency.ltc:
// isValid = (value.length == 34);
// break;
// case CryptoCurrency.nano:
// isValid = (value.length == 64)||(value.length == 65);
// break;
// case CryptoCurrency.trx:
// isValid = (value.length == 34);
// break;
// case CryptoCurrency.usdt:
// isValid = (value.length == 42);
// break;
// case CryptoCurrency.xlm:
// isValid = (value.length == 56);
// break;
// case CryptoCurrency.xrp:
// isValid = (value.length == 34);
// break;
// }
// }
//
// errorMessage = isValid ? null : S.current.error_text_address;
// }
//}
+8 -10
View File
@@ -33,10 +33,8 @@ abstract class ExchangeStoreBase with Store {
provider = initialProvider;
depositCurrency = initialDepositCurrency;
receiveCurrency = initialReceiveCurrency;
/*isDepositAddressEnabled = !(depositCurrency == walletStore.type);
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);*/
isDepositAddressEnabled = true;
isReceiveAddressEnabled = true;
isDepositAddressEnabled = !(depositCurrency == walletStore.type);
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);
depositAmount = '';
receiveAmount = '';
depositAddress = '';
@@ -109,16 +107,16 @@ abstract class ExchangeStoreBase with Store {
void changeDepositCurrency({CryptoCurrency currency}) {
depositCurrency = currency;
_onPairChange();
/*isDepositAddressEnabled = !(depositCurrency == walletStore.type);
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);*/
isDepositAddressEnabled = !(depositCurrency == walletStore.type);
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);
}
@action
void changeReceiveCurrency({CryptoCurrency currency}) {
receiveCurrency = currency;
_onPairChange();
/*isDepositAddressEnabled = !(depositCurrency == walletStore.type);
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);*/
isDepositAddressEnabled = !(depositCurrency == walletStore.type);
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);
}
@action
@@ -242,8 +240,8 @@ abstract class ExchangeStoreBase with Store {
receiveCurrency = CryptoCurrency.btc;
depositAddress = depositCurrency == walletStore.type ? walletStore.address : '';
receiveAddress = receiveCurrency == walletStore.type ? walletStore.address : '';
/*isDepositAddressEnabled = !(depositCurrency == walletStore.type);
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);*/
isDepositAddressEnabled = !(depositCurrency == walletStore.type);
isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);
_onPairChange();
}
@@ -72,8 +72,7 @@ abstract class NodeListBase with Store {
Future<bool> isNodeOnline(Node node) async {
try {
return await node.requestNode(node.uri,
login: node.login, password: node.password);
return await node.requestNode();
} catch (e) {
return false;
}
+107 -95
View File
@@ -38,103 +38,115 @@ class AddressTextField extends StatelessWidget {
@override
Widget build(BuildContext context) {
return TextFormField(
onFieldSubmitted: (_) => FocusScope.of(context).unfocus(),
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 +
(spaceBetweenPrefixIcons * options.length),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 5),
if (this.options.contains(AddressTextFieldOption.qrCode)) ...[
Container(
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _presentQRScanner(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset('assets/images/qr_code_icon.png')),
))
],
if (this
.options
.contains(AddressTextFieldOption.addressBook)) ...[
Container(
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _presetAddressBookPicker(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/open_book.png')),
))
],
if (this
.options
.contains(AddressTextFieldOption.subaddressList)) ...[
Container(
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _presetSubaddressListPicker(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/receive_icon_raw.png')),
))
],
],
return Stack(
children: <Widget>[
TextFormField(
onFieldSubmitted: (_) => FocusScope.of(context).unfocus(),
enabled: isActive,
controller: controller,
focusNode: focusNode,
style: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.title.color
),
),
hintStyle: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.caption.color
),
hintText: placeholder ?? S.current.widgets_address,
focusedBorder: isBorderExist
? UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))
: InputBorder.none,
disabledBorder: isBorderExist
? UnderlineInputBorder(
borderSide:
BorderSide(color: Theme.of(context).dividerColor, width: 1.0))
: InputBorder.none,
enabledBorder: isBorderExist
? UnderlineInputBorder(
borderSide:
decoration: InputDecoration(
suffixIcon: SizedBox(
width: prefixIconWidth * options.length +
(spaceBetweenPrefixIcons * options.length),
),
hintStyle: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.caption.color
),
hintText: placeholder ?? S.current.widgets_address,
focusedBorder: isBorderExist
? UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))
: InputBorder.none,
disabledBorder: isBorderExist
? UnderlineInputBorder(
borderSide:
BorderSide(color: Theme.of(context).dividerColor, width: 1.0))
: InputBorder.none,
),
validator: validator,
: InputBorder.none,
enabledBorder: isBorderExist
? UnderlineInputBorder(
borderSide:
BorderSide(color: Theme.of(context).dividerColor, width: 1.0))
: InputBorder.none,
),
validator: validator,
),
Positioned(
bottom: 10,
right: 0,
child: SizedBox(
width: prefixIconWidth * options.length +
(spaceBetweenPrefixIcons * options.length),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 5),
if (this.options.contains(AddressTextFieldOption.qrCode)) ...[
Container(
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _presentQRScanner(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset('assets/images/qr_code_icon.png')),
))
],
if (this
.options
.contains(AddressTextFieldOption.addressBook)) ...[
Container(
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _presetAddressBookPicker(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/open_book.png')),
))
],
if (this
.options
.contains(AddressTextFieldOption.subaddressList)) ...[
Container(
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _presetSubaddressListPicker(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/receive_icon_raw.png')),
))
],
],
),
)
)
],
);
}
+172
View File
@@ -0,0 +1,172 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class StandardListRow extends StatelessWidget {
StandardListRow(
{@required this.title, @required this.isSelected, this.onTap});
final String title;
final bool isSelected;
final void Function(BuildContext context) onTap;
@override
Widget build(BuildContext context) {
final leading = buildLeading(context);
final trailing = buildTrailing(context);
return InkWell(
onTap: () => onTap?.call(context),
child: Container(
color: _backgroundColor(context),
height: 56,
padding: EdgeInsets.only(left: 24, right: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
if (leading != null) leading,
buildCenter(context, hasLeftOffset: leading != null),
if (trailing != null) trailing
])));
}
Widget buildLeading(BuildContext context) => null;
Widget buildCenter(BuildContext context, {@required bool hasLeftOffset}) {
// FIXME: find better way for keep text on left side.
return Expanded(
child: Row(mainAxisAlignment: MainAxisAlignment.start, children: [
if (hasLeftOffset) SizedBox(width: 10),
Text(title,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: _titleColor(context)))
]));
}
Widget buildTrailing(BuildContext context) => null;
Color _titleColor(BuildContext context) => isSelected
? Color.fromRGBO(20, 200, 71, 1)
: Theme.of(context).primaryTextTheme.title.color;
Color _backgroundColor(BuildContext context) {
// return Theme.of(context).accentTextTheme.subtitle.decorationColor;
return Theme.of(context).accentTextTheme.title.backgroundColor;
}
}
class SectionHeaderListRow extends StatelessWidget {
@override
Widget build(BuildContext context) => Column(children: [
StandardListSeparator(),
Container(width: double.infinity, height: 40, color: Colors.white),
StandardListSeparator()
]);
}
class StandardListSeparator extends StatelessWidget {
StandardListSeparator({this.padding});
final EdgeInsets padding;
@override
Widget build(BuildContext context) {
return Container(
height: 1,
padding: padding,
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: Container(height: 1, color: Color.fromRGBO(219, 227, 243, 1)));
}
}
class StandardList extends StatelessWidget {
StandardList({@required this.itemCount, @required this.itemBuilder});
final int itemCount;
final IndexedWidgetBuilder itemBuilder;
@override
Widget build(BuildContext context) {
return ListView.separated(
separatorBuilder: (_, __) =>
StandardListSeparator(padding: EdgeInsets.only(left: 24)),
itemCount: itemCount,
itemBuilder: itemBuilder);
}
}
class SectionStandardListItem {
SectionStandardListItem({this.hasFullSeparator = false, this.child});
final bool hasFullSeparator;
final Widget child;
}
class SectionStandardList extends StatelessWidget {
SectionStandardList(
{@required this.itemCounter,
@required this.itemBuilder,
@required this.sectionCount,
BuildContext context})
: totalRows = transform(context, sectionCount, itemCounter, itemBuilder);
final int sectionCount;
final int Function(int sectionIndex) itemCounter;
final Widget Function(BuildContext context, int sectionIndex, int itemIndex)
itemBuilder;
final List<Widget> totalRows;
static List<Widget> transform(
BuildContext context,
int sectionCount,
int Function(int sectionIndex) itemCounter,
Widget Function(BuildContext context, int sectionIndex, int itemIndex)
itemBuilder) {
final items = <Widget>[];
for (var sectionIndex = 0; sectionIndex < sectionCount; sectionIndex++) {
if (sectionIndex == 0) {
items.add(StandardListSeparator());
}
final itemCount = itemCounter(sectionIndex);
for (var itemIndex = 0; itemIndex < itemCount; itemIndex++) {
final item = itemBuilder(context, sectionIndex, itemIndex);
items.add(item);
}
items.add(sectionIndex + 1 != sectionCount
? SectionHeaderListRow()
: StandardListSeparator());
}
return items;
}
@override
Widget build(BuildContext context) {
return ListView.separated(
separatorBuilder: (_, index) {
final row = totalRows[index];
if (row is StandardListSeparator || row is SectionHeaderListRow) {
return Container();
}
final nextRow = totalRows[index + 1];
// If current row is pre last and last row is separator.
if (nextRow is StandardListSeparator ||
nextRow is SectionHeaderListRow) {
return Container();
}
return StandardListSeparator(padding: EdgeInsets.only(left: 24));
},
itemCount: totalRows.length,
itemBuilder: (_, index) => totalRows[index]);
}
}
+26 -17
View File
@@ -1,7 +1,11 @@
import 'package:flutter/material.dart';
class StandartListRow extends StatelessWidget {
StandartListRow({this.title, this.value, this.isDrawTop, this.isDrawBottom});
StandartListRow(
{this.title,
this.value,
this.isDrawTop = false,
this.isDrawBottom = false});
final String title;
final String value;
@@ -13,17 +17,18 @@ class StandartListRow extends StatelessWidget {
return Column(
children: <Widget>[
isDrawTop
? Container(
width: double.infinity,
height: 1,
color: Theme.of(context).dividerColor,
)
: Offstage(),
? 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),
padding:
const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
@@ -31,26 +36,30 @@ class StandartListRow extends StatelessWidget {
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.caption.color),
color:
Theme.of(context).primaryTextTheme.caption.color),
textAlign: TextAlign.left),
Padding(
padding: const EdgeInsets.only(top: 12),
child: Text(value,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color)),
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme
.title
.color)),
)
]),
),
),
isDrawBottom
? Container(
width: double.infinity,
height: 1,
color: Theme.of(context).dividerColor,
)
: Offstage(),
? Container(
width: double.infinity,
height: 1,
color: Theme.of(context).dividerColor,
)
: Offstage(),
],
);
}
+31
View File
@@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
class TrailButton extends StatelessWidget {
TrailButton({
@required this.caption,
@required this.onPressed
});
final String caption;
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return ButtonTheme(
minWidth: double.minPositive,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: FlatButton(
padding: EdgeInsets.all(0),
child: Text(
caption,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontWeight: FontWeight.w500,
fontSize: 14),
),
onPressed: onPressed),
);
}
}