This commit is contained in:
M
2020-06-20 10:10:00 +03:00
parent 719842964b
commit 81cee186db
94 changed files with 3786 additions and 3001 deletions
+3 -1
View File
@@ -1 +1,3 @@
abstract class Balance {}
abstract class Balance {
const Balance();
}
+11
View File
@@ -0,0 +1,11 @@
class MnemonicItem {
MnemonicItem({String text}) : _text = text;
String get text => _text;
String _text;
void changeText(String text) => _text = text;
@override
String toString() => text;
}
-17
View File
@@ -1,17 +0,0 @@
class MnemoticItem {
MnemoticItem({String text, this.dic}) : _text = text;
String get text => _text;
final List<String> dic;
String _text;
bool isCorrect() => dic.contains(text);
void changeText(String text) {
_text = text;
}
@override
String toString() => text;
}
@@ -1,5 +1,4 @@
import 'dart:async';
import 'package:cake_wallet/bitcoin/bitcoin_wallet.manager.dart';
import 'package:cake_wallet/bitcoin/key.dart';
import 'package:cake_wallet/src/domain/common/wallet_info.dart';
import 'package:flutter/foundation.dart';
@@ -119,7 +118,7 @@ class WalletListService {
MoneroWalletsManager(walletInfoSource: walletInfoSource);
break;
case WalletType.bitcoin:
walletsManager = BitcoinWalletManager();
// walletsManager = BitcoinWalletManager();
break;
case WalletType.none:
walletsManager = null;
+4 -4
View File
@@ -35,10 +35,10 @@ void setReactions(
settingsStore: settingsStore,
priceStore: priceStore);
autorun((_) async {
if (authenticationStore.state == AuthenticationState.allowed) {
await loginStore.loadCurrentWallet();
authenticationStore.state = AuthenticationState.readyToLogin;
}
// if (authenticationStore.state == AuthenticationState.allowed) {
// await loginStore.loadCurrentWallet();
// authenticationStore.state = AuthenticationState.readyToLogin;
// }
});
}
+65 -52
View File
@@ -1,10 +1,9 @@
import 'package:mobx/mobx.dart';
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/stores/auth/auth_state.dart';
import 'package:cake_wallet/src/stores/auth/auth_store.dart';
import 'package:cake_wallet/view_model/auth_state.dart';
import 'package:cake_wallet/view_model/auth_view_model.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/src/domain/common/biometric_auth.dart';
@@ -12,8 +11,12 @@ import 'package:cake_wallet/src/domain/common/biometric_auth.dart';
typedef OnAuthenticationFinished = void Function(bool, AuthPageState);
class AuthPage extends StatefulWidget {
AuthPage({this.onAuthenticationFinished, this.closable = true});
AuthPage(
{this.onAuthenticationFinished,
this.authViewModel,
this.closable = true});
final AuthViewModel authViewModel;
final OnAuthenticationFinished onAuthenticationFinished;
final bool closable;
@@ -25,40 +28,13 @@ class AuthPageState extends State<AuthPage> {
final _key = GlobalKey<ScaffoldState>();
final _pinCodeKey = GlobalKey<PinCodeState>();
final _backArrowImageDarkTheme =
Image.asset('assets/images/back_arrow_dark_theme.png');
void changeProcessText(String text) {
_key.currentState.showSnackBar(
SnackBar(content: Text(text), backgroundColor: Colors.green));
}
void close() => Navigator.of(_key.currentContext).pop();
Image.asset('assets/images/back_arrow_dark_theme.png');
ReactionDisposer _reaction;
@override
Widget build(BuildContext context) {
final authStore = Provider.of<AuthStore>(context);
final settingsStore = Provider.of<SettingsStore>(context);
if (settingsStore.allowBiometricalAuthentication) {
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,
),
);
}
}
);
});
}
reaction((_) => authStore.state, (AuthState state) {
void initState() {
_reaction ??=
reaction((_) => widget.authViewModel.state, (AuthState state) {
if (state is AuthenticatedSuccessfully) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (widget.onAuthenticationFinished != null) {
@@ -119,32 +95,69 @@ class AuthPageState extends State<AuthPage> {
});
}
});
super.initState();
}
@override
void dispose() {
_reaction.reaction.dispose();
super.dispose();
}
void changeProcessText(String text) => _key.currentState.showSnackBar(
SnackBar(content: Text(text), backgroundColor: Colors.green));
void close() => Navigator.of(_key.currentContext).pop();
@override
Widget build(BuildContext context) {
// final authStore = Provider.of<AuthStore>(context);
// final settingsStore = Provider.of<SettingsStore>(context);
// if (settingsStore.allowBiometricalAuthentication) {
// 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,
// ),
// );
// }
// }
// );
// });
// }
return Scaffold(
key: _key,
appBar: CupertinoNavigationBar(
leading: widget.closable
? SizedBox(
height: 37,
width: 20,
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: () => Navigator.of(context).pop(),
child: _backArrowImageDarkTheme),
),
)
: Container(),
? SizedBox(
height: 37,
width: 20,
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: () => Navigator.of(context).pop(),
child: _backArrowImageDarkTheme),
),
)
: Container(),
backgroundColor: Theme.of(context).backgroundColor,
border: null,
),
resizeToAvoidBottomPadding: false,
body: PinCode(
(pin, _) => authStore.auth(
password: pin.fold('', (ac, val) => ac + '$val')),
(pin, _) => widget.authViewModel
.auth(password: pin.fold('', (ac, val) => ac + '$val')),
false,
_pinCodeKey));
}
+13 -12
View File
@@ -14,15 +14,16 @@ Widget createLoginPage(
@required WalletService walletService,
@required WalletListService walletListService,
@required AuthenticationStore authenticationStore}) =>
Provider(
create: (_) => AuthStore(
sharedPreferences: sharedPreferences,
userService: userService,
walletService: walletService),
child: AuthPage(
onAuthenticationFinished: (isAuthenticated, state) {
if (isAuthenticated) {
authenticationStore.loggedIn();
}
},
closable: false));
null;
// Provider(
// create: (_) => AuthStore(
// sharedPreferences: sharedPreferences,
// userService: userService,
// walletService: walletService),
// child: AuthPage(
// onAuthenticationFinished: (isAuthenticated, state) {
// if (isAuthenticated) {
// authenticationStore.loggedIn();
// }
// },
// closable: false));
+11 -10
View File
@@ -11,13 +11,14 @@ Widget createUnlockPage(
@required UserService userService,
@required WalletService walletService,
@required Function(bool, AuthPageState) onAuthenticationFinished}) =>
WillPopScope(
onWillPop: () async => false,
child: Provider(
create: (_) => AuthStore(
sharedPreferences: sharedPreferences,
userService: userService,
walletService: walletService),
child: AuthPage(
onAuthenticationFinished: onAuthenticationFinished,
closable: false)));
null;
// WillPopScope(
// onWillPop: () async => false,
// child: Provider(
// create: (_) => AuthStore(
// sharedPreferences: sharedPreferences,
// userService: userService,
// walletService: walletService),
// child: AuthPage(
// onAuthenticationFinished: onAuthenticationFinished,
// closable: false)));
+18 -13
View File
@@ -10,12 +10,19 @@ enum AppBarStyle { regular, withShadow }
abstract class BasePage extends StatelessWidget {
String get title => null;
bool get isModalBackButton => false;
Color get backgroundLightColor => Colors.white;
Color get backgroundDarkColor => PaletteDark.darkNightBlue;
bool get resizeToAvoidBottomPadding => true;
AppBarStyle get appBarStyle => AppBarStyle.regular;
Widget Function(BuildContext, Widget) get rootWrapper => null;
final _backArrowImage = Image.asset('assets/images/back_arrow.png');
final _backArrowImageDarkTheme =
Image.asset('assets/images/back_arrow_dark_theme.png');
@@ -83,9 +90,8 @@ abstract class BasePage extends StatelessWidget {
leading: leading(context),
middle: middle(context),
trailing: trailing(context),
backgroundColor: _isDarkTheme
? backgroundDarkColor
: backgroundLightColor);
backgroundColor:
_isDarkTheme ? backgroundDarkColor : backgroundLightColor);
case AppBarStyle.withShadow:
return NavBar.withShadow(
@@ -93,9 +99,8 @@ abstract class BasePage extends StatelessWidget {
leading: leading(context),
middle: middle(context),
trailing: trailing(context),
backgroundColor: _isDarkTheme
? backgroundDarkColor
: backgroundLightColor);
backgroundColor:
_isDarkTheme ? backgroundDarkColor : backgroundLightColor);
default:
return NavBar(
@@ -103,9 +108,8 @@ abstract class BasePage extends StatelessWidget {
leading: leading(context),
middle: middle(context),
trailing: trailing(context),
backgroundColor: _isDarkTheme
? backgroundDarkColor
: backgroundLightColor);
backgroundColor:
_isDarkTheme ? backgroundDarkColor : backgroundLightColor);
}
}
@@ -116,13 +120,14 @@ abstract class BasePage extends StatelessWidget {
final _themeChanger = Provider.of<ThemeChanger>(context);
final _isDarkTheme = _themeChanger.getTheme() == Themes.darkTheme;
return Scaffold(
backgroundColor: _isDarkTheme
? backgroundDarkColor
: backgroundLightColor,
final root = Scaffold(
backgroundColor:
_isDarkTheme ? backgroundDarkColor : backgroundLightColor,
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
appBar: appBar(context),
body: SafeArea(child: body(context)),
floatingActionButton: floatingActionButton(context));
return rootWrapper?.call(context, root) ?? root;
}
}
+21 -23
View File
@@ -1,28 +1,36 @@
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/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 {
DashboardPage({@required this.walletViewModel});
final DashboardViewModel walletViewModel;
final _bodyKey = GlobalKey();
@override
Widget build(BuildContext context) => DashboardPageBody(key: _bodyKey);
Widget build(BuildContext context) =>
DashboardPageBody(key: _bodyKey, walletViewModel: walletViewModel);
}
class DashboardPageBody extends StatefulWidget {
DashboardPageBody({Key key}) : super(key: key);
DashboardPageBody({Key key, @required this.walletViewModel})
: super(key: key);
final DashboardViewModel walletViewModel;
@override
DashboardPageBodyState createState() => DashboardPageBodyState();
}
class DashboardPageBodyState extends State<DashboardPageBody> {
@override
Widget build(BuildContext context) {
final menuButton = Image.asset('assets/images/header.png',
final menuButton = Image.asset(
'assets/images/header.png',
color: Theme.of(context).primaryTextTheme.title.color,
);
@@ -30,15 +38,10 @@ class DashboardPageBodyState extends State<DashboardPageBody> {
child: Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).scaffoldBackgroundColor,
Theme.of(context).primaryColor
],
begin: Alignment.centerLeft,
end: Alignment.centerRight
)
),
gradient: LinearGradient(colors: [
Theme.of(context).scaffoldBackgroundColor,
Theme.of(context).primaryColor
], begin: Alignment.centerLeft, end: Alignment.centerRight)),
child: Column(
children: <Widget>[
Container(
@@ -55,22 +58,17 @@ class DashboardPageBodyState extends State<DashboardPageBody> {
padding: EdgeInsets.all(0),
onPressed: () async {
await showDialog<void>(
builder: (_) => MenuWidget(),
context: context
);
builder: (_) => MenuWidget(), context: context);
},
child: menuButton),
),
),
),
Padding(
padding: EdgeInsets.only(left: 20, top: 20),
child: WalletCard(),
),
SizedBox(
height: 28,
),
Expanded(child: TradeHistoryPanel())
padding: EdgeInsets.only(left: 20, top: 20),
child: WalletCard(walletVM: widget.walletViewModel)),
SizedBox(height: 28),
Expanded(child: TradeHistoryPanel(dashboardViewModel: widget.walletViewModel))
],
),
),
@@ -17,7 +17,7 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
final actionListStore = Provider.of<ActionListStore>(context);
// final actionListStore = Provider.of<ActionListStore>(context);
final historyPanelWidth = MediaQuery.of(context).size.width;
final _themeChanger = Provider.of<ThemeChanger>(context);
@@ -97,44 +97,44 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
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: 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:
@@ -156,17 +156,17 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
.spaceBetween,
children: [
Text('XMR.TO'),
Checkbox(
value: actionListStore
.tradeFilterStore
.displayXMRTO,
onChanged: (value) =>
actionListStore
.tradeFilterStore
.toggleDisplayExchange(
ExchangeProviderDescription
.xmrto),
)
// Checkbox(
// value: actionListStore
// .tradeFilterStore
// .displayXMRTO,
// onChanged: (value) =>
// actionListStore
// .tradeFilterStore
// .toggleDisplayExchange(
// ExchangeProviderDescription
// .xmrto),
// )
]))),
PopupMenuItem(
value: 4,
@@ -177,17 +177,17 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
.spaceBetween,
children: [
Text('Change.NOW'),
Checkbox(
value: actionListStore
.tradeFilterStore
.displayChangeNow,
onChanged: (value) =>
actionListStore
.tradeFilterStore
.toggleDisplayExchange(
ExchangeProviderDescription
.changeNow),
)
// Checkbox(
// value: actionListStore
// .tradeFilterStore
// .displayChangeNow,
// onChanged: (value) =>
// actionListStore
// .tradeFilterStore
// .toggleDisplayExchange(
// ExchangeProviderDescription
// .changeNow),
// )
]))),
PopupMenuItem(
value: 5,
@@ -198,17 +198,17 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
.spaceBetween,
children: [
Text('MorphToken'),
Checkbox(
value: actionListStore
.tradeFilterStore
.displayMorphToken,
onChanged: (value) =>
actionListStore
.tradeFilterStore
.toggleDisplayExchange(
ExchangeProviderDescription
.morphToken),
)
// Checkbox(
// value: actionListStore
// .tradeFilterStore
// .displayMorphToken,
// onChanged: (value) =>
// actionListStore
// .tradeFilterStore
// .toggleDisplayExchange(
// ExchangeProviderDescription
// .morphToken),
// )
])))
],
child: filterButton,
@@ -225,10 +225,10 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
.add(Duration(days: 1)));
if (picked != null && picked.length == 2) {
actionListStore.transactionFilterStore
.changeStartDate(picked.first);
actionListStore.transactionFilterStore
.changeEndDate(picked.last);
// actionListStore.transactionFilterStore
// .changeStartDate(picked.first);
// actionListStore.transactionFilterStore
// .changeEndDate(picked.last);
}
}
},
@@ -1,21 +1,26 @@
import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
import 'package:cake_wallet/src/stores/action_list/action_list_store.dart';
import 'package:cake_wallet/src/stores/action_list/date_section_item.dart';
import 'package:cake_wallet/src/stores/action_list/trade_list_item.dart';
import 'package:cake_wallet/src/stores/action_list/transaction_list_item.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/view_model/dashboard_view_model.dart';
import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
import 'package:cake_wallet/src/stores/action_list/action_list_store.dart';
import 'package:cake_wallet/src/stores/action_list/date_section_item.dart';
import 'package:cake_wallet/src/stores/action_list/trade_list_item.dart';
import 'package:cake_wallet/src/stores/action_list/transaction_list_item.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'date_section_raw.dart';
import 'trade_row.dart';
import 'transaction_raw.dart';
import 'button_header.dart';
class TradeHistoryPanel extends StatefulWidget {
TradeHistoryPanel({this.dashboardViewModel});
DashboardViewModel dashboardViewModel;
@override
TradeHistoryPanelState createState() => TradeHistoryPanelState();
}
@@ -44,119 +49,127 @@ class TradeHistoryPanelState extends State<TradeHistoryPanel> {
@override
Widget build(BuildContext context) {
final actionListStore = Provider.of<ActionListStore>(context);
final settingsStore = Provider.of<SettingsStore>(context);
final transactionDateFormat = DateFormat("HH:mm");
// final actionListStore = Provider.of<ActionListStore>(context);
// final settingsStore = Provider.of<SettingsStore>(context);
final transactionDateFormat = DateFormat('HH:mm');
return Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
alignment: Alignment.bottomCenter,
child: AnimatedContainer(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
height: panelHeight,
duration: Duration(milliseconds: 1000),
curve: Curves.fastOutSlowIn,
child: ClipRRect(
borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)),
child: CustomScrollView(
slivers: <Widget>[
SliverPersistentHeader(
delegate: ButtonHeader(),
pinned: true,
floating: false,
),
Observer(
key: _listObserverKey,
builder: (_) {
final items = actionListStore.items == null
? <String>[]
: actionListStore.items;
final itemsCount = items.length + 1;
final symbol = settingsStore.fiatCurrency.toString();
double freeSpaceHeight = MediaQuery.of(context).size.height - 496;
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 SliverList(
key: _listKey,
delegate: SliverChildBuilderDelegate(
(context, index) {
return SliverList(
key: _listKey,
delegate:
SliverChildBuilderDelegate((context, index) {
if (index == itemsCount - 1) {
freeSpaceHeight = freeSpaceHeight >= 0
? freeSpaceHeight
: 0;
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(
height: freeSpaceHeight,
width: MediaQuery.of(context).size.width,
color: Theme.of(context).backgroundColor,
);
}
final item = items[index];
if (item is DateSectionItem) {
freeSpaceHeight -= 38;
return DateSectionRaw(date: item.date);
}
if (item is TransactionListItem) {
freeSpaceHeight -= 62;
final transaction = item.transaction;
final savedDisplayMode = settingsStore.balanceDisplayMode;
final formattedAmount =
savedDisplayMode == BalanceDisplayMode.hiddenBalance
? '---'
: transaction.amountFormatted();
final formattedFiatAmount =
savedDisplayMode == BalanceDisplayMode.hiddenBalance
? '---'
: transaction.fiatAmount(); // symbol ???
return TransactionRow(
onTap: () => Navigator.of(context).pushNamed(
Routes.transactionDetails,
arguments: transaction),
direction: transaction.direction,
formattedDate:
transactionDateFormat.format(transaction.date),
formattedAmount: formattedAmount,
formattedFiatAmount: formattedFiatAmount,
isPending: transaction.isPending);
}
if (item is TradeListItem) {
freeSpaceHeight -= 62;
final trade = item.trade;
final savedDisplayMode = settingsStore.balanceDisplayMode;
final formattedAmount = trade.amount != null
? savedDisplayMode == BalanceDisplayMode.hiddenBalance
? '---'
: trade.amountFormatted()
: trade.amount;
return TradeRow(
onTap: () => Navigator.of(context)
.pushNamed(Routes.tradeDetails, arguments: trade),
provider: trade.provider,
from: trade.from,
to: trade.to,
createdAtFormattedDate:
transactionDateFormat.format(trade.createdAt),
formattedAmount: formattedAmount);
}
return Container(
color: Theme.of(context).backgroundColor
);
},
childCount: itemsCount
)
);
})
],
),
)
),
);
color: Theme.of(context).backgroundColor);
}, childCount: itemsCount));
})
],
)))); //,
}
}
}
+342 -317
View File
@@ -1,21 +1,27 @@
import 'dart:async';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/domain/common/balance_display_mode.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:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/sync_status.dart';
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/view_model/dashboard_view_model.dart';
class WalletCard extends StatefulWidget {
WalletCard({this.walletVM});
DashboardViewModel walletVM;
@override
WalletCardState createState() => WalletCardState();
}
@@ -50,14 +56,12 @@ class WalletCardState extends State<WalletCard> {
cardWidth = screenWidth;
opacity = 1;
});
Timer(Duration(milliseconds: 500), () =>
setState(() => isDraw = true)
);
Timer(Duration(milliseconds: 500), () => setState(() => isDraw = true));
}
@override
Widget build(BuildContext context) {
final List<Color> colorsSync = [
final colorsSync = [
Theme.of(context).cardTheme.color,
Theme.of(context).hoverColor
];
@@ -67,77 +71,67 @@ class WalletCardState extends State<WalletCard> {
height: cardHeight,
alignment: Alignment.centerRight,
child: AnimatedContainer(
alignment: Alignment.centerLeft,
width: cardWidth,
height: cardHeight,
duration: Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn,
padding: EdgeInsets.only(
top: 1,
left: 1,
bottom: 1
),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
color: Theme.of(context).focusColor,
boxShadow: [
BoxShadow(
color: PaletteDark.darkNightBlue.withOpacity(0.5),
blurRadius: 8,
offset: Offset(5, 5))
]
),
child: ClipRRect(
borderRadius: BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
child: Container(
width: cardWidth,
height: cardHeight,
color: Theme.of(context).cardColor,
child: InkWell(
onTap: () => setState(() => isFrontSide = !isFrontSide),
child: isFrontSide
? frontSide(colorsSync)
: backSide(colorsSync)
alignment: Alignment.centerLeft,
width: cardWidth,
height: cardHeight,
duration: Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn,
padding: EdgeInsets.only(top: 1, left: 1, bottom: 1),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
bottomLeft: Radius.circular(10)),
color: Theme.of(context).focusColor,
boxShadow: [
BoxShadow(
color: PaletteDark.darkNightBlue.withOpacity(0.5),
blurRadius: 8,
offset: Offset(5, 5))
]),
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
child: Container(
width: cardWidth,
height: cardHeight,
color: Theme.of(context).cardColor,
child: InkWell(
onTap: () => setState(() => isFrontSide = !isFrontSide),
child: isFrontSide
? frontSide(colorsSync)
: backSide(colorsSync)),
),
),
)
),
)),
);
}
Widget frontSide(List<Color> colorsSync) {
final syncStore = Provider.of<SyncStore>(context);
final walletStore = Provider.of<WalletStore>(context);
// final 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',
// final balanceStore = Provider.of<BalanceStore>(context);
final triangleButton = Image.asset(
'assets/images/triangle.png',
color: Theme.of(context).primaryTextTheme.title.color,
);
return Observer(
key: _syncingObserverKey,
builder: (_) {
final status = syncStore.status;
final status = widget.walletVM.status;
final statusText = status.title();
final progress = syncStore.status.progress();
final progress = status.progress();
final indicatorWidth = progress * cardWidth;
String shortAddress = walletStore.subaddress.address;
shortAddress = shortAddress.replaceRange(4, shortAddress.length - 4, '...');
final shortAddress = widget.walletVM.address
.replaceRange(4, widget.walletVM.address.length - 4, '...');
var descriptionText = '';
if (status is SyncingSyncStatus) {
descriptionText = S
.of(context)
.Blocks_remaining(
syncStore.status.toString());
descriptionText = S.of(context).Blocks_remaining(status.toString());
}
if (status is FailedSyncStatus) {
descriptionText = S
.of(context)
.please_try_to_connect_to_another_node;
descriptionText = S.of(context).please_try_to_connect_to_another_node;
}
return Container(
@@ -149,183 +143,206 @@ class WalletCardState extends State<WalletCard> {
height: cardHeight,
width: indicatorWidth,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
bottomLeft: Radius.circular(10)),
gradient: LinearGradient(
colors: colorsSync,
begin: Alignment.topCenter,
end: Alignment.bottomCenter
)
),
end: Alignment.bottomCenter)),
),
progress != 1
? Positioned(
left: indicatorWidth,
top: 0,
bottom: 0,
child: Container(
width: 1,
height: cardHeight,
color: Theme.of(context).focusColor,
)
)
: Offstage(),
isDraw ? Positioned(
left: 20,
right: 20,
top: 30,
bottom: 30,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
? Positioned(
left: indicatorWidth,
top: 0,
bottom: 0,
child: Container(
width: 1,
height: cardHeight,
color: Theme.of(context).focusColor,
))
: Offstage(),
isDraw
? Positioned(
left: 20,
right: 20,
top: 30,
bottom: 30,
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
InkWell(
onTap: () {},
child: Row(
children: <Widget>[
Text(
walletStore.name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InkWell(
onTap: () {},
child: Row(
children: <Widget>[
Text(
widget.walletVM.name,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
),
SizedBox(width: 10),
triangleButton
],
),
SizedBox(width: 10),
triangleButton
],
),
),
SizedBox(height: 5),
if (widget.walletVM.subname?.isNotEmpty ?? false)
Text(
widget.walletVM.subname,
style: TextStyle(
fontSize: 12,
color: Theme.of(context)
.primaryTextTheme
.caption
.color),
)
],
),
SizedBox(
height: 5,
),
Text(
walletStore.account.label,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).primaryTextTheme.caption.color
Container(
width: 98,
height: 32,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context)
.accentTextTheme
.subtitle
.backgroundColor,
borderRadius: BorderRadius.all(
Radius.circular(16))),
child: Text(
shortAddress,
style: TextStyle(
fontSize: 12,
color: Theme.of(context)
.primaryTextTheme
.caption
.color),
),
)
],
),
Container(
width: 98,
height: 32,
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context).accentTextTheme.subtitle.backgroundColor,
borderRadius: BorderRadius.all(Radius.circular(16))
),
child: Text(
shortAddress,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).primaryTextTheme.caption.color
),
),
)
],
),
status is SyncedSyncStatus
? Observer(
key: _balanceObserverKey,
builder: (_) {
final balanceDisplayMode = settingsStore.balanceDisplayMode;
final symbol = settingsStore
.fiatCurrency
.toString();
var balance = '---';
var fiatBalance = '---';
status is SyncedSyncStatus
? Observer(
key: _balanceObserverKey,
builder: (_) {
final balanceDisplayMode =
BalanceDisplayMode.fullBalance;
// settingsStore.balanceDisplayMode;
final symbol =
settingsStore.fiatCurrency.toString();
var balance = '---';
var fiatBalance = '---';
if (balanceDisplayMode ==
BalanceDisplayMode.availableBalance) {
balance =
balanceStore.unlockedBalance ??
'0.0';
fiatBalance =
'$symbol ${balanceStore.fiatUnlockedBalance}';
}
if (balanceDisplayMode ==
BalanceDisplayMode.availableBalance) {
balance = widget.walletVM.balance
.unlockedBalance ??
'0.0';
fiatBalance = '\$ 123.43';
// '$symbol ${balanceStore.fiatUnlockedBalance}';
}
if (balanceDisplayMode ==
BalanceDisplayMode.fullBalance) {
balance =
balanceStore.fullBalance ?? '0.0';
fiatBalance =
'$symbol ${balanceStore.fiatFullBalance}';
}
if (balanceDisplayMode ==
BalanceDisplayMode.fullBalance) {
balance = widget.walletVM.balance
.totalBalance ??
'0.0';
fiatBalance = '\$ 123.43';
// '$symbol ${balanceStore.fiatFullBalance}';
}
return Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
return Row(
crossAxisAlignment:
CrossAxisAlignment.end,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Text(
balanceDisplayMode.toString(),
style: TextStyle(
fontSize: 12,
color: Theme.of(context)
.primaryTextTheme
.caption
.color),
),
SizedBox(height: 5),
Text(
balance,
style: TextStyle(
fontSize: 28,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
)
],
),
Text(
fiatBalance,
style: TextStyle(
fontSize: 14,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
)
],
);
})
: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
balanceDisplayMode.toString(),
style: TextStyle(
fontSize: 12,
color: Theme.of(context).primaryTextTheme.caption.color
),
),
SizedBox(height: 5),
Text(
balance,
style: TextStyle(
fontSize: 28,
color: Theme.of(context).primaryTextTheme.title.color
),
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Text(
statusText,
style: TextStyle(
fontSize: 12,
color: Theme.of(context)
.primaryTextTheme
.caption
.color),
),
SizedBox(height: 5),
Text(
descriptionText,
style: TextStyle(
fontSize: 14,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
)
],
)
],
),
Text(
fiatBalance,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryTextTheme.title.color
),
)
],
);
}
)
: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
statusText,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).primaryTextTheme.caption.color
),
),
SizedBox(height: 5),
Text(
descriptionText,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryTextTheme.title.color
),
)
],
)
],
)
],
),
)
)
: Offstage()
),
))
: Offstage()
],
),
);
@@ -334,149 +351,157 @@ class WalletCardState extends State<WalletCard> {
}
Widget backSide(List<Color> colorsSync) {
final walletStore = Provider.of<WalletStore>(context);
final rightArrow = Image.asset('assets/images/right_arrow.png',
final rightArrow = Image.asset(
'assets/images/right_arrow.png',
color: Theme.of(context).primaryTextTheme.title.color,
);
double messageBoxHeight = 0;
double messageBoxWidth = cardWidth - 10;
var messageBoxHeight = 0.0;
var messageBoxWidth = cardWidth - 10;
return Observer(
key: _addressObserverKey,
builder: (_) {
return Container(
width: cardWidth,
height: cardHeight,
alignment: Alignment.topCenter,
child: Stack(
alignment: Alignment.topRight,
children: <Widget>[
Container(
width: cardWidth,
height: cardHeight,
padding: EdgeInsets.only(left: 20, right: 20, top: 30, bottom: 30),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
gradient: LinearGradient(
colors: colorsSync,
begin: Alignment.topCenter,
end: Alignment.bottomCenter
)
),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Container(
key: _addressObserverKey,
builder: (_) {
return Container(
width: cardWidth,
height: cardHeight,
alignment: Alignment.topCenter,
child: Stack(
alignment: Alignment.topRight,
children: <Widget>[
Container(
width: cardWidth,
height: cardHeight,
padding:
EdgeInsets.only(left: 20, right: 20, top: 30, bottom: 30),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(10),
bottomLeft: Radius.circular(10)),
gradient: LinearGradient(
colors: colorsSync,
begin: Alignment.topCenter,
end: Alignment.bottomCenter)),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Container(
height: 90,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
S.current.card_address,
style: TextStyle(
fontSize: 12,
color: Theme.of(context).primaryTextTheme.caption.color
),
color: Theme.of(context)
.primaryTextTheme
.caption
.color),
),
SizedBox(height: 10),
GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(
text: walletStore.subaddress.address));
_addressObserverKey.currentState.setState(() {
text: widget.walletVM.address));
_addressObserverKey.currentState
.setState(() {
messageBoxHeight = 20;
messageBoxWidth = cardWidth;
});
Timer(Duration(milliseconds: 1000), () {
try {
_addressObserverKey.currentState.setState(() {
_addressObserverKey.currentState
.setState(() {
messageBoxHeight = 0;
messageBoxWidth = cardWidth - 10;
});
} catch(e) {
} catch (e) {
print('${e.toString()}');
}
});
},
child: Text(
walletStore.subaddress.address,
widget.walletVM.address,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryTextTheme.title.color
),
color: Theme.of(context)
.primaryTextTheme
.title
.color),
),
)
],
),
)),
SizedBox(width: 10),
Container(
width: 90,
height: 90,
child: QrImage(
data: widget.walletVM.address,
backgroundColor: Colors.transparent,
foregroundColor: Theme.of(context)
.primaryTextTheme
.caption
.color),
)
),
SizedBox(width: 10),
Container(
width: 90,
height: 90,
child: QrImage(
data: walletStore.subaddress.address,
backgroundColor: Colors.transparent,
foregroundColor: Theme.of(context).primaryTextTheme.caption.color
),
)
],
),
Container(
height: 44,
padding: EdgeInsets.only(left: 20, right: 20),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(22)),
color: Theme.of(context).primaryTextTheme.overline.color
],
),
child: InkWell(
onTap: () => Navigator.of(context,
rootNavigator: true)
.pushNamed(Routes.receive),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
S.of(context).accounts_subaddresses,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryTextTheme.title.color
Container(
height: 44,
padding: EdgeInsets.only(left: 20, right: 20),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(22)),
color: Theme.of(context)
.primaryTextTheme
.overline
.color),
child: InkWell(
onTap: () =>
Navigator.of(context, rootNavigator: true)
.pushNamed(Routes.receive),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
S.of(context).accounts_subaddresses,
style: TextStyle(
fontSize: 14,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
),
),
rightArrow
],
rightArrow
],
),
),
),
)
],
),
),
AnimatedContainer(
width: messageBoxWidth,
height: messageBoxHeight,
alignment: Alignment.center,
duration: Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn,
decoration: BoxDecoration(
borderRadius: BorderRadius.only(topLeft: Radius.circular(10)),
color: Colors.green
),
child: Text(
S.of(context).copied_to_clipboard,
style: TextStyle(
fontSize: 10,
color: Colors.white
)
],
),
),
)
],
),
);
}
);
AnimatedContainer(
width: messageBoxWidth,
height: messageBoxHeight,
alignment: Alignment.center,
duration: Duration(milliseconds: 500),
curve: Curves.fastOutSlowIn,
decoration: BoxDecoration(
borderRadius:
BorderRadius.only(topLeft: Radius.circular(10)),
color: Colors.green),
child: Text(
S.of(context).copied_to_clipboard,
style: TextStyle(fontSize: 10, color: Colors.white),
),
)
],
),
);
});
}
}
}
+75 -120
View File
@@ -1,85 +1,54 @@
import 'package:cake_wallet/core/monero_wallet_list_service.dart';
import 'package:cake_wallet/core/wallet_creation_service.dart';
import 'package:cake_wallet/core/wallet_credentials.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:mobx/mobx.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/stores/wallet_creation/wallet_creation_store.dart';
import 'package:cake_wallet/src/stores/wallet_creation/wallet_creation_state.dart';
import 'package:cake_wallet/src/domain/services/wallet_list_service.dart';
import 'package:cake_wallet/src/domain/services/wallet_service.dart';
import 'package:cake_wallet/core/validator.dart';
import 'package:cake_wallet/src/widgets/seed_language_selector.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/stores/seed_language/seed_language_store.dart';
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/view_model/wallet_creation_state.dart';
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
class NewWalletPage extends BasePage {
NewWalletPage(
{@required this.walletsService,
@required this.walletService,
@required this.sharedPreferences});
NewWalletPage(this._walletNewVM);
final WalletListService walletsService;
final WalletService walletService;
final SharedPreferences sharedPreferences;
final WalletNewVM _walletNewVM;
@override
String get title => S.current.new_wallet;
@override
Widget body(BuildContext context) => WalletNameForm();
Widget body(BuildContext context) => WalletNameForm(_walletNewVM);
}
class WalletNameForm extends StatefulWidget {
WalletNameForm(this._walletNewVM);
final WalletNewVM _walletNewVM;
@override
_WalletNameFormState createState() => _WalletNameFormState();
_WalletNameFormState createState() => _WalletNameFormState(_walletNewVM);
}
class _WalletNameFormState extends State<WalletNameForm> {
_WalletNameFormState(this._walletNewVM);
static const aspectRatioImage = 1.22;
final _formKey = GlobalKey<FormState>();
final nameController = TextEditingController();
final walletNameImage = Image.asset('assets/images/wallet_name.png');
final _formKey = GlobalKey<FormState>();
final _languageSelectorKey = GlobalKey<SeedLanguageSelectorState>();
ReactionDisposer _stateReaction;
final WalletNewVM _walletNewVM;
@override
void dispose() {
nameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final walletCreationStore = Provider.of<WalletCreationStore>(context);
final walletCreationService = Provider.of<WalletCreationService>(context);
// FIXME: Does seed language store is really needed ???
final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
final seedLocales = [
S.current.seed_language_english,
S.current.seed_language_chinese,
S.current.seed_language_dutch,
S.current.seed_language_german,
S.current.seed_language_japanese,
S.current.seed_language_portuguese,
S.current.seed_language_russian,
S.current.seed_language_spanish
];
nameController.addListener(() =>
walletCreationStore.setDisabledStatus(!nameController.text.isNotEmpty));
reaction((_) => walletCreationStore.state, (WalletCreationState state) {
void initState() {
_stateReaction ??=
reaction((_) => _walletNewVM.state, (WalletCreationState state) {
if (state is WalletCreatedSuccessfully) {
Navigator.of(context).popUntil((route) => route.isFirst);
}
@@ -98,7 +67,11 @@ class _WalletNameFormState extends State<WalletNameForm> {
});
}
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.only(top: 24),
child: ScrollableWithBottomSection(
@@ -116,94 +89,76 @@ class _WalletNameFormState extends State<WalletNameForm> {
child: Form(
key: _formKey,
child: TextFormField(
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color),
controller: nameController,
decoration: InputDecoration(
hintStyle: TextStyle(
fontSize: 16.0,
color: Theme.of(context)
.primaryTextTheme
.caption
.color),
hintText: S.of(context).wallet_name,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
validator: (value) {
walletCreationStore.validateWalletName(value);
return walletCreationStore.errorMessage;
},
)),
onChanged: (value) => _walletNewVM.name = value,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w600,
color:
Theme.of(context).primaryTextTheme.title.color),
decoration: InputDecoration(
hintStyle: TextStyle(
fontSize: 16.0,
color: Theme.of(context)
.primaryTextTheme
.caption
.color),
hintText: S.of(context).wallet_name,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
validator: WalletNameValidator())),
),
Padding(
padding: EdgeInsets.only(top: 40),
child: Text(
S.of(context).seed_language_choose,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color),
if (_walletNewVM.hasLanguageSelector) ...[
Padding(
padding: EdgeInsets.only(top: 40),
child: Text(
S.of(context).seed_language_choose,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color),
),
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: Observer(
builder: (_) => SelectButton(
image: null,
text: seedLocales[seedLanguages
.indexOf(seedLanguageStore.selectedSeedLanguage)],
color: Theme.of(context)
.accentTextTheme
.title
.backgroundColor,
textColor: Theme.of(context).primaryTextTheme.title.color,
onTap: () async => await showDialog(
context: context,
builder: (BuildContext context) =>
SeedLanguagePicker()))),
)
Padding(
padding: EdgeInsets.only(top: 24),
child: SeedLanguageSelector(
key: _languageSelectorKey,
initialSelected: defaultSeedLanguage),
)
]
]),
bottomSectionPadding:
EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: () => _confirmForm(walletCreationService,
seedLanguageStore.selectedSeedLanguage),
onPressed: _confirmForm,
text: S.of(context).continue_text,
color: Colors.green,
textColor: Colors.white,
isLoading: walletCreationStore.state is WalletIsCreating,
isDisabled: walletCreationStore.isDisabledStatus,
isLoading: _walletNewVM.state is WalletCreatedSuccessfully,
isDisabled: _walletNewVM.name.isEmpty,
);
},
)),
);
}
void _confirmForm(
WalletCreationService walletCreationService, String language) {
void _confirmForm() {
if (!_formKey.currentState.validate()) {
return;
}
WalletCredentials credentials;
if (walletCreationService.type == WalletType.monero) {
credentials = MoneroNewWalletCredentials(
name: nameController.text, language: language);
}
walletCreationService.create(credentials);
_walletNewVM.create(
options: _walletNewVM.hasLanguageSelector
? _languageSelectorKey.currentState.selected
: null);
}
}
@@ -1,3 +1,4 @@
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/generated/i18n.dart';
@@ -8,14 +9,23 @@ import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
import 'package:cake_wallet/routes.dart';
class NewWalletTypePage extends BasePage {
NewWalletTypePage({this.onTypeSelected});
final void Function(BuildContext, WalletType) onTypeSelected;
@override
String get title => S.current.new_wallet;
@override
Widget body(BuildContext context) => WalletTypeForm();
Widget body(BuildContext context) =>
WalletTypeForm(onTypeSelected: onTypeSelected);
}
class WalletTypeForm extends StatefulWidget {
WalletTypeForm({this.onTypeSelected});
final void Function(BuildContext, WalletType) onTypeSelected;
@override
WalletTypeFormState createState() => WalletTypeFormState();
}
@@ -23,35 +33,19 @@ class WalletTypeForm extends StatefulWidget {
class WalletTypeFormState extends State<WalletTypeForm> {
static const aspectRatioImage = 1.22;
final moneroIcon = Image.asset('assets/images/monero.png', height: 24, width: 24);
final bitcoinIcon = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
final moneroIcon =
Image.asset('assets/images/monero.png', height: 24, width: 24);
final bitcoinIcon =
Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
bool isDisabledButton;
bool isMoneroSelected;
bool isBitcoinSelected;
Color moneroBackgroundColor = Colors.transparent;
Color moneroTextColor = Colors.transparent;
Color bitcoinBackgroundColor = Colors.transparent;
Color bitcoinTextColor = Colors.transparent;
WalletType selected;
List<WalletType> types;
@override
void initState() {
isDisabledButton = true;
isMoneroSelected = false;
isBitcoinSelected = false;
types = [WalletType.bitcoin, WalletType.monero];
super.initState();
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
}
void afterLayout(dynamic _) {
moneroBackgroundColor = Theme.of(context).accentTextTheme.title.backgroundColor;
moneroTextColor = Theme.of(context).primaryTextTheme.title.color;
bitcoinBackgroundColor = Theme.of(context).accentTextTheme.title.backgroundColor;
bitcoinTextColor = Theme.of(context).primaryTextTheme.title.color;
setState(() {});
}
@override
@@ -75,71 +69,52 @@ class WalletTypeFormState extends State<WalletTypeForm> {
S.of(context).choose_wallet_currency,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color
),
fontSize: 16,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color),
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: SelectButton(
image: bitcoinIcon,
text: 'Bitcoin',
color: bitcoinBackgroundColor,
textColor: bitcoinTextColor,
onTap: () {}),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: SelectButton(
image: moneroIcon,
text: 'Monero',
color: moneroBackgroundColor,
textColor: moneroTextColor,
onTap: () => onSelectMoneroButton(context)),
)
...types.map((type) => Padding(
padding: EdgeInsets.only(top: 24),
child: SelectButton(
image: _iconFor(type),
text: walletTypeToString(type),
color: _backgroundColorFor(selected == type),
textColor: _textColorFor(selected == type),
onTap: () => setState(() => selected = type)),
))
],
),
bottomSectionPadding: EdgeInsets.only(
left: 24,
right: 24,
bottom: 24
),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: PrimaryButton(
onPressed: () => Navigator.of(context).pushNamed(Routes.newWallet),
onPressed: () => widget.onTypeSelected(context, selected),
text: S.of(context).seed_language_next,
color: Colors.green,
textColor: Colors.white,
isDisabled: isDisabledButton,
isDisabled: selected == null,
),
),
);
}
void onSelectMoneroButton(BuildContext context) {
isMoneroSelected = true;
isBitcoinSelected = false;
isDisabledButton = false;
// FIXME: Move color selection inside ui element; add isSelected to buttons.
moneroBackgroundColor = Theme.of(context).accentTextTheme.title.decorationColor;
moneroTextColor = Theme.of(context).primaryTextTheme.title.backgroundColor;
bitcoinBackgroundColor = Theme.of(context).accentTextTheme.title.backgroundColor;
bitcoinTextColor = Theme.of(context).primaryTextTheme.title.color;
Color _backgroundColorFor(bool isSelected) => isSelected
? Theme.of(context).accentTextTheme.title.decorationColor
: Theme.of(context).accentTextTheme.title.backgroundColor;
setState(() {});
Color _textColorFor(bool isSelected) => isSelected
? Theme.of(context).primaryTextTheme.title.backgroundColor
: Theme.of(context).primaryTextTheme.title.color;
Image _iconFor(WalletType type) {
switch (type) {
case WalletType.monero:
return moneroIcon;
case WalletType.bitcoin:
return bitcoinIcon;
default:
return null;
}
}
void onSelectBitcoinButton(BuildContext context) {
isMoneroSelected = false;
isBitcoinSelected = true;
isDisabledButton = false;
moneroBackgroundColor = Theme.of(context).accentTextTheme.title.backgroundColor;
moneroTextColor = Theme.of(context).primaryTextTheme.title.color;
bitcoinBackgroundColor = moneroBackgroundColor = Theme.of(context).accentTextTheme.title.decorationColor;
bitcoinTextColor = Theme.of(context).primaryTextTheme.title.backgroundColor;
setState(() {});
}
}
}
+234 -323
View File
@@ -1,3 +1,5 @@
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';
@@ -16,347 +18,256 @@ 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';
class ReceivePage extends StatefulWidget {
@override
ReceivePageState createState() => ReceivePageState();
}
class ReceivePage extends BasePage {
ReceivePage({this.addressListViewModel})
: amountController = TextEditingController(),
_formKey = GlobalKey<FormState>() {
amountController.addListener(() => addressListViewModel.amount =
_formKey.currentState.validate() ? amountController.text : '');
}
class ReceivePageState extends State<ReceivePage> {
final amountController = TextEditingController();
final _formKey = GlobalKey<FormState>();
final _backArrowImage = Image.asset('assets/images/back_arrow.png');
final _backArrowImageDarkTheme =
Image.asset('assets/images/back_arrow_dark_theme.png');
final AddressListViewModel addressListViewModel;
final TextEditingController amountController;
final GlobalKey<FormState> _formKey;
@override
void dispose() {
amountController.dispose();
super.dispose();
Color get backgroundLightColor => Colors.transparent;
@override
Color get backgroundDarkColor => Colors.transparent;
@override
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.topLeft, end: Alignment.bottomRight)),
child: scaffold);
@override
Widget middle(BuildContext context) => Text(
S.of(context).receive,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color),
);
@override
Widget trailing(BuildContext context) {
final shareImage = Image.asset('assets/images/share.png',
color: Theme.of(context).primaryTextTheme.title.color);
return SizedBox(
height: 20.0,
width: 14.0,
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: () => Share.text(S.current.share_address,
addressListViewModel.address.address, 'text/plain'),
child: shareImage),
),
);
}
@override
Widget build(BuildContext context) {
final walletStore = Provider.of<WalletStore>(context);
final subaddressListStore = Provider.of<SubaddressListStore>(context);
final accountListStore = Provider.of<AccountListStore>(context);
return super.build(context);
}
final shareImage = Image.asset('assets/images/share.png',
color: Theme.of(context).primaryTextTheme.title.color,
);
@override
Widget body(BuildContext context) {
final copyImage = Image.asset('assets/images/copy_content.png',
color: Theme.of(context).primaryTextTheme.title.color,
);
color: Theme.of(context).primaryTextTheme.title.color);
final currentColor = Theme.of(context).accentTextTheme.subtitle.decorationColor;
final notCurrentColor = Theme.of(context).backgroundColor;
final currentTextColor = Colors.blue;
final notCurrentTextColor = Theme.of(context).primaryTextTheme.caption.color;
final _themeChanger = Provider.of<ThemeChanger>(context);
Image _backButton;
if (_themeChanger.getTheme() == Themes.darkTheme) {
_backButton = _backArrowImageDarkTheme;
} else {
_backButton = _backArrowImage;
}
amountController.addListener(() {
if (_formKey.currentState.validate()) {
walletStore.onChangedAmountValue(amountController.text);
} else {
walletStore.onChangedAmountValue('');
}
});
return Scaffold(
resizeToAvoidBottomPadding: false,
body: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.only(top: 24),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).scaffoldBackgroundColor,
Theme.of(context).primaryColor
],
begin: Alignment.centerLeft,
end: Alignment.centerRight
)
return SingleChildScrollView(
child: Column(
children: <Widget>[
SizedBox(height: 25),
Row(children: <Widget>[
Spacer(flex: 4),
Observer(
builder: (_) => Flexible(
flex: 6,
child: Center(
child: AspectRatio(
aspectRatio: 1.0,
child: QrImage(
data: addressListViewModel.uri.toString(),
backgroundColor: Colors.transparent,
foregroundColor: Theme.of(context)
.primaryTextTheme
.display4
.color,
))))),
Spacer(flex: 4)
]),
Padding(
padding: EdgeInsets.fromLTRB(24, 40, 24, 0),
child: Row(
children: <Widget>[
Expanded(
child: Form(
key: _formKey,
child: BaseTextFormField(
controller: amountController,
keyboardType:
TextInputType.numberWithOptions(decimal: true),
inputFormatters: [
BlacklistingTextInputFormatter(
RegExp('[\\-|\\ |\\,]'))
],
textAlign: TextAlign.center,
hintText: S.of(context).receive_amount,
borderColor: Theme.of(context)
.primaryTextTheme
.headline5
.color
.withOpacity(0.4),
validator: AmountValidator(),
autovalidate: true,
placeholderTextStyle: TextStyle(
color: Theme.of(context)
.primaryTextTheme
.headline5
.color,
fontSize: 20,
fontWeight: FontWeight.w600))))
],
),
),
child: Column(
children: <Widget>[
Container(
padding: EdgeInsets.only(
top: 10,
bottom: 20,
left: 5,
right: 10
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 44,
width: 44,
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: () => Navigator.of(context).pop(),
child: _backButton),
),
),
Text(
S.of(context).receive,
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color),
),
SizedBox(
height: 44.0,
width: 44.0,
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
padding: EdgeInsets.all(0),
onPressed: () => Share.text(
S.current.share_address, walletStore.subaddress.address, 'text/plain'),
child: shareImage),
),
)
],
),
),
Expanded(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Observer(builder: (_) {
return Row(
children: <Widget>[
Spacer(
flex: 1,
),
Flexible(
flex: 2,
child: Center(
child: AspectRatio(
aspectRatio: 1.0,
child: QrImage(
data: walletStore.subaddress.address +
walletStore.amountValue,
backgroundColor: Colors.transparent,
foregroundColor: Theme.of(context).primaryTextTheme.display4.color,
),
),
)),
Spacer(
flex: 1,
)
],
);
}),
Padding(
padding: EdgeInsets.all(24),
child: Row(
children: <Widget>[
Expanded(
child: Form(
key: _formKey,
child: BaseTextFormField(
controller: amountController,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [
BlacklistingTextInputFormatter(
RegExp('[\\-|\\ |\\,]'))
],
textAlign: TextAlign.center,
hintText: S.of(context).receive_amount,
borderColor: Theme.of(context).primaryTextTheme.caption.color,
validator: (value) {
walletStore.validateAmount(value);
return walletStore.errorMessage;
},
autovalidate: true,
)
)
)
],
),
),
Padding(
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
child: Builder(
builder: (context) => Observer(
builder: (context) => GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(
text: walletStore.subaddress.address));
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
S.of(context).copied_to_clipboard,
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 500),
));
},
child: Container(
height: 48,
padding: EdgeInsets.only(left: 24, right: 24),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(24)),
color: Theme.of(context).primaryTextTheme.overline.color
),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Text(
walletStore.subaddress.address,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color
),
),
),
Padding(
padding: EdgeInsets.only(left: 12),
child: copyImage,
)
],
Padding(
padding: EdgeInsets.only(left: 24, right: 24, bottom: 40, top: 40),
child: Builder(
builder: (context) => Observer(
builder: (context) => GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(
text: addressListViewModel.address.address));
Scaffold.of(context).showSnackBar(SnackBar(
content: Text(
S.of(context).copied_to_clipboard,
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 500),
));
},
child: Container(
height: 48,
padding: EdgeInsets.only(left: 24, right: 24),
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(24)),
color: Theme.of(context)
.primaryTextTheme
.overline
.color),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Text(
addressListViewModel.address.address,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: Theme.of(context)
.primaryTextTheme
.title
.color),
),
),
)
)
),
),
Observer(
builder: (_) => ListView.separated(
separatorBuilder: (context, index) => Divider(
height: 1,
color: Theme.of(context).dividerColor,
),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: subaddressListStore.subaddresses.length + 2,
itemBuilder: (context, index) {
Padding(
padding: EdgeInsets.only(left: 12),
child: copyImage,
)
],
),
),
))),
),
Observer(
builder: (_) => ListView.separated(
separatorBuilder: (context, _) =>
Divider(height: 1, color: Theme.of(context).dividerColor),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemCount: addressListViewModel.items.length,
itemBuilder: (context, index) {
final item = addressListViewModel.items[index];
Widget cell = Container();
if (index == 0) {
return ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24)
),
child: HeaderTile(
onTap: () async {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AccountListPage(accountListStore: accountListStore);
}
);
},
title: walletStore.account.label,
icon: Icon(
Icons.arrow_forward_ios,
size: 14,
color: Theme.of(context).primaryTextTheme.title.color,
)
),
);
}
if (item is AccountListHeader) {
cell = HeaderTile(
onTap: () async {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
// return AccountListPage(
// accountListStore:
// accountListStore);
});
},
title: addressListViewModel.accountLabel,
icon: Icon(
Icons.arrow_forward_ios,
size: 14,
color:
Theme.of(context).primaryTextTheme.title.color,
));
}
if (index == 1) {
return HeaderTile(
onTap: () => Navigator.of(context)
.pushNamed(Routes.newSubaddress),
title: S.of(context).subaddresses,
icon: Icon(
Icons.add,
size: 20,
color: Theme.of(context).primaryTextTheme.title.color,
)
);
}
if (item is AddressListHeader) {
cell = HeaderTile(
onTap: () => Navigator.of(context)
.pushNamed(Routes.newSubaddress),
title: S.of(context).subaddresses,
icon: Icon(
Icons.add,
size: 20,
color:
Theme.of(context).primaryTextTheme.title.color,
));
}
index -= 2;
if (item is AddressListItem) {
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)));
}
return Observer(
builder: (_) {
final subaddress = subaddressListStore.subaddresses[index];
final isCurrent =
walletStore.subaddress.address == subaddress.address;
final label = subaddress.label.isNotEmpty
? subaddress.label
: subaddress.address;
final content = InkWell(
onTap: () => walletStore.setSubaddress(subaddress),
child: Container(
color: isCurrent ? currentColor : notCurrentColor,
padding: EdgeInsets.only(
left: 24,
right: 24,
top: 28,
bottom: 28
),
child: Text(
label,
style: TextStyle(
fontSize: subaddress.label.isNotEmpty
? 18 : 10,
fontWeight: FontWeight.bold,
color: isCurrent
? currentTextColor
: notCurrentTextColor,
),
),
),
);
return isCurrent
? content
: Slidable(
key: Key(subaddress.address),
actionPane: SlidableDrawerActionPane(),
child: content,
secondaryActions: <Widget>[
IconSlideAction(
caption: S.of(context).edit,
color: Theme.of(context).primaryTextTheme.overline.color,
icon: Icons.edit,
onTap: () => Navigator.of(context)
.pushNamed(Routes.newSubaddress, arguments: subaddress),
)
]
);
}
);
}
)
),
],
),
)
)
],
)
return index != 0
? cell
: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(24),
topRight: Radius.circular(24)),
child: cell,
);
})),
],
),
);
}
}
}
@@ -0,0 +1,70 @@
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';
class AddressCell extends StatelessWidget {
factory AddressCell.fromItem(AddressListItem item,
{@required bool isCurrent,
Function(String) onTap,
Function() onEdit}) =>
AddressCell(
address: item.address,
name: item.name,
isCurrent: isCurrent,
onTap: onTap,
onEdit: onEdit);
AddressCell(
{@required this.address,
@required this.name,
@required this.isCurrent,
this.onTap,
this.onEdit});
final String address;
final String name;
final bool isCurrent;
final Function(String) onTap;
final Function() onEdit;
String get label => name ?? address;
@override
Widget build(BuildContext context) {
const currentTextColor = Colors.blue; // FIXME: Why it's defined here ?
final currentColor =
Theme.of(context).accentTextTheme.subtitle.decorationColor;
final notCurrentColor = Theme.of(context).backgroundColor;
final notCurrentTextColor =
Theme.of(context).primaryTextTheme.caption.color;
final Widget cell = InkWell(
onTap: () => onTap(address),
child: Container(
color: isCurrent ? currentColor : notCurrentColor,
padding: EdgeInsets.only(left: 24, right: 24, top: 28, bottom: 28),
child: Text(
name ?? address,
style: TextStyle(
fontSize: name?.isNotEmpty ?? false ? 18 : 10,
fontWeight: FontWeight.bold,
color: isCurrent ? currentTextColor : notCurrentTextColor,
),
),
));
return isCurrent
? cell
: Slidable(
key: Key(address),
actionPane: SlidableDrawerActionPane(),
child: cell,
secondaryActions: <Widget>[
IconSlideAction(
caption: S.of(context).edit,
color: Theme.of(context).primaryTextTheme.overline.color,
icon: Icons.edit,
onTap: () => onEdit?.call())
]);
}
}
@@ -1,26 +1,35 @@
import 'package:mobx/mobx.dart';
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/generated/i18n.dart';
import 'package:cake_wallet/src/stores/wallet_restoration/wallet_restoration_store.dart';
import 'package:cake_wallet/src/stores/wallet_restoration/wallet_restoration_state.dart';
import 'package:cake_wallet/core/validator.dart';
import 'package:cake_wallet/view_model/wallet_creation_state.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
class RestoreWalletFromSeedDetailsPage extends BasePage {
RestoreWalletFromSeedDetailsPage(
{@required this.walletRestorationFromSeedVM});
final WalletRestorationFromSeedVM walletRestorationFromSeedVM;
@override
String get title => S.current.restore_wallet_restore_description;
@override
Widget body(BuildContext context) => RestoreFromSeedDetailsForm();
Widget body(BuildContext context) => RestoreFromSeedDetailsForm(
walletRestorationFromSeedVM: walletRestorationFromSeedVM);
}
class RestoreFromSeedDetailsForm extends StatefulWidget {
RestoreFromSeedDetailsForm({@required this.walletRestorationFromSeedVM});
final WalletRestorationFromSeedVM walletRestorationFromSeedVM;
@override
_RestoreFromSeedDetailsFormState createState() =>
_RestoreFromSeedDetailsFormState();
@@ -31,31 +40,17 @@ class _RestoreFromSeedDetailsFormState
final _formKey = GlobalKey<FormState>();
final _blockchainHeightKey = GlobalKey<BlockchainHeightState>();
final _nameController = TextEditingController();
ReactionDisposer _stateReaction;
@override
void dispose() {
_nameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final walletRestorationStore = Provider.of<WalletRestorationStore>(context);
_nameController.addListener(() {
if (_nameController.text.isNotEmpty) {
walletRestorationStore.setDisabledState(false);
} else {
walletRestorationStore.setDisabledState(true);
}
});
reaction((_) => walletRestorationStore.state, (WalletRestorationState state) {
if (state is WalletRestoredSuccessfully) {
void initState() {
_stateReaction = reaction((_) => widget.walletRestorationFromSeedVM.state,
(WalletCreationState state) {
if (state is WalletCreatedSuccessfully) {
Navigator.of(context).popUntil((route) => route.isFirst);
}
if (state is WalletRestorationFailure) {
if (state is WalletCreationFailure) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showDialog<void>(
context: context,
@@ -64,73 +59,87 @@ class _RestoreFromSeedDetailsFormState
alertTitle: S.current.restore_title_from_seed,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
buttonAction: () => Navigator.of(context).pop());
});
});
}
});
_nameController.addListener(
() => widget.walletRestorationFromSeedVM.name = _nameController.text);
super.initState();
}
@override
void dispose() {
_nameController.dispose();
_stateReaction.reaction.dispose();
super.dispose();
}
@override
Widget build(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(
child: Column(children: <Widget>[
Row(
children: <Widget>[
Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(top: 20.0),
child: TextFormField(
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
controller: _nameController,
decoration: InputDecoration(
hintStyle: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: S.of(context).restore_wallet_name,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
validator: (value) {
walletRestorationStore
.validateWalletName(value);
return walletRestorationStore.errorMessage;
},
),
))
],
),
BlockchainHeightWidget(key: _blockchainHeightKey),
]),
Flexible(
child: Container(
padding: EdgeInsets.only(top: 20.0),
child: TextFormField(
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color),
controller: _nameController,
decoration: InputDecoration(
hintStyle: TextStyle(
color: Theme.of(context)
.primaryTextTheme
.caption
.color,
fontSize: 16),
hintText: S.of(context).restore_wallet_name,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
validator: WalletNameValidator(),
),
))
],
),
if (widget.walletRestorationFromSeedVM.hasRestorationHeight)
BlockchainHeightWidget(
key: _blockchainHeightKey,
onHeightChange: (height) {
widget.walletRestorationFromSeedVM.height = height;
print(height);
}),
]),
),
bottomSectionPadding: EdgeInsets.only(bottom: 24),
bottomSection: Observer(builder: (_) {
return LoadingPrimaryButton(
onPressed: () {
if (_formKey.currentState.validate()) {
walletRestorationStore.restoreFromSeed(
name: _nameController.text,
restoreHeight: _blockchainHeightKey.currentState.height);
widget.walletRestorationFromSeedVM.create();
}
},
isLoading: walletRestorationStore.state is WalletIsRestoring,
isLoading:
widget.walletRestorationFromSeedVM.state is WalletCreating,
text: S.of(context).restore_recover,
color: Colors.green,
textColor: Colors.white,
isDisabled: walletRestorationStore.disabledState,
isDisabled: _nameController.text.isNotEmpty,
);
}),
),
@@ -1,27 +1,19 @@
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/services/wallet_list_service.dart';
import 'package:cake_wallet/src/domain/services/wallet_service.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/stores/wallet_restoration/wallet_restoration_store.dart';
import 'package:cake_wallet/src/widgets/seed_widget.dart';
import 'package:cake_wallet/src/stores/seed_language/seed_language_store.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:cake_wallet/core/seed_validator.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/core/mnemonic_length.dart';
class RestoreWalletFromSeedPage extends BasePage {
RestoreWalletFromSeedPage(
{@required this.walletsService,
@required this.walletService,
@required this.sharedPreferences});
RestoreWalletFromSeedPage({@required this.type, @required this.language});
final WalletListService walletsService;
final WalletService walletService;
final SharedPreferences sharedPreferences;
final WalletType type;
final String language;
final formKey = GlobalKey<_RestoreFromSeedFormState>();
@override
@@ -34,11 +26,14 @@ class RestoreWalletFromSeedPage extends BasePage {
Color get backgroundDarkColor => PaletteDark.lightNightBlue;
@override
Widget body(BuildContext context) => RestoreFromSeedForm(key: formKey);
Widget body(BuildContext context) =>
RestoreFromSeedForm(key: formKey, type: type, language: language);
}
class RestoreFromSeedForm extends StatefulWidget {
RestoreFromSeedForm({Key key}) : super(key: key);
RestoreFromSeedForm({Key key, this.type, this.language}) : super(key: key);
final WalletType type;
final String language;
@override
_RestoreFromSeedFormState createState() => _RestoreFromSeedFormState();
@@ -46,13 +41,11 @@ class RestoreFromSeedForm extends StatefulWidget {
class _RestoreFromSeedFormState extends State<RestoreFromSeedForm> {
final _seedKey = GlobalKey<SeedWidgetState>();
void clear() => _seedKey.currentState.clear();
String mnemonic() => _seedKey.currentState.items.map((e) => e.text).join(' ');
@override
Widget build(BuildContext context) {
final walletRestorationStore = Provider.of<WalletRestorationStore>(context);
final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
return GestureDetector(
onTap: () =>
SystemChannels.textInput.invokeMethod<void>('TextInput.hide'),
@@ -60,11 +53,13 @@ class _RestoreFromSeedFormState extends State<RestoreFromSeedForm> {
color: Theme.of(context).backgroundColor,
child: SeedWidget(
key: _seedKey,
onMnemoticChange: (seed) => walletRestorationStore.setSeed(seed),
maxLength: mnemonicLength(widget.type),
onMnemonicChange: (seed) => null,
onFinish: () => Navigator.of(context).pushNamed(
Routes.restoreWalletFromSeedDetails,
arguments: _seedKey.currentState.items),
seedLanguage: seedLanguageStore.selectedSeedLanguage,
arguments: [widget.type, widget.language, mnemonic()]),
validator:
SeedValidator(type: widget.type, language: widget.language),
),
),
);
@@ -1,19 +1,21 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/restore/widgets/restore_button.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/stores/seed_language/seed_language_store.dart';
import 'package:provider/provider.dart';
class RestoreWalletOptionsPage extends BasePage {
RestoreWalletOptionsPage({@required this.type});
RestoreWalletOptionsPage(
{@required this.type,
@required this.onRestoreFromSeed,
@required this.onRestoreFromKeys});
static const _aspectRatioImage = 2.086;
final WalletType type;
final Function(BuildContext context) onRestoreFromSeed;
final Function(BuildContext context) onRestoreFromKeys;
@override
String get title => S.current.restore_seed_keys_restore;
@@ -23,8 +25,6 @@ class RestoreWalletOptionsPage extends BasePage {
@override
Widget body(BuildContext context) {
final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
return Container(
width: double.infinity,
height: double.infinity,
@@ -33,28 +33,56 @@ class RestoreWalletOptionsPage extends BasePage {
child: Column(
children: <Widget>[
RestoreButton(
onPressed: () {
seedLanguageStore
.setCurrentRoute(Routes.restoreWalletFromSeed);
Navigator.pushNamed(context, Routes.seedLanguage);
},
onPressed: () => onRestoreFromSeed(context),
image: imageSeed,
title: S.of(context).restore_title_from_seed,
description: S.of(context).restore_description_from_seed),
description: _fromSeedDescription(context)),
Padding(
padding: EdgeInsets.only(top: 24),
child: RestoreButton(
onPressed: () {
seedLanguageStore
.setCurrentRoute(Routes.restoreWalletFromKeys);
Navigator.pushNamed(context, Routes.seedLanguage);
},
onPressed: () => onRestoreFromKeys(context),
image: imageKeys,
title: S.of(context).restore_title_from_keys,
description: S.of(context).restore_description_from_keys),
title: _fromKeyTitle(context),
description: _fromKeyDescription(context)),
)
],
),
));
}
String _fromSeedDescription(BuildContext context) {
switch (type) {
case WalletType.monero:
return S.of(context).restore_description_from_seed;
case WalletType.bitcoin:
// TODO: Add transaction for bitcoin description.
return 'Restore your wallet from 12 word combination code';
default:
return '';
}
}
String _fromKeyDescription(BuildContext context) {
switch (type) {
case WalletType.monero:
return S.of(context).restore_description_from_keys;
case WalletType.bitcoin:
// TODO: Add transaction for bitcoin description.
return 'Restore your wallet from generated WIF string from your private keys';
default:
return '';
}
}
String _fromKeyTitle(BuildContext context) {
switch (type) {
case WalletType.monero:
return S.of(context).restore_title_from_keys;
case WalletType.bitcoin:
// TODO: Add transaction for bitcoin description.
return 'Restore from WIF';
default:
return '';
}
}
}
+65 -45
View File
@@ -1,10 +1,15 @@
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:hive/hive.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
import 'package:cake_wallet/store/authentication_store.dart';
//import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
import 'package:cake_wallet/src/stores/price/price_store.dart';
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
@@ -21,7 +26,10 @@ import 'package:cake_wallet/src/screens/auth/auth_page.dart';
import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
class Root extends StatefulWidget {
Root({Key key}) : super(key: key);
Root({Key key, this.authenticationStore, this.appStore}) : super(key: key);
final AuthenticationStore authenticationStore;
final AppStore appStore;
@override
RootState createState() => RootState();
@@ -30,7 +38,6 @@ class Root extends StatefulWidget {
class RootState extends State<Root> with WidgetsBindingObserver {
bool _isInactive;
bool _postFrameCallback;
AuthenticationStore _authenticationStore;
@override
void initState() {
@@ -48,12 +55,12 @@ class RootState extends State<Root> with WidgetsBindingObserver {
return;
}
if (!_isInactive &&
_authenticationStore.state ==
AuthenticationState.authenticated ||
_authenticationStore.state == AuthenticationState.active) {
setState(() => _isInactive = true);
}
// if (!_isInactive &&
// widget.authenticationStore.state ==
// AuthenticationState.authenticated ||
// widget.authenticationStore.state == AuthenticationState.active) {
// setState(() => _isInactive = true);
// }
break;
default:
@@ -63,18 +70,18 @@ class RootState extends State<Root> with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
_authenticationStore = Provider.of<AuthenticationStore>(context);
final sharedPreferences = Provider.of<SharedPreferences>(context);
final walletListService = Provider.of<WalletListService>(context);
final walletService = Provider.of<WalletService>(context);
final userService = Provider.of<UserService>(context);
final priceStore = Provider.of<PriceStore>(context);
final authenticationStore = Provider.of<AuthenticationStore>(context);
final trades = Provider.of<Box<Trade>>(context);
final transactionDescriptions =
Provider.of<Box<TransactionDescription>>(context);
final walletStore = Provider.of<WalletStore>(context);
final settingsStore = Provider.of<SettingsStore>(context);
// _authenticationStore = Provider.of<AuthenticationStore>(context);
// final sharedPreferences = Provider.of<SharedPreferences>(context);
// final walletListService = Provider.of<WalletListService>(context);
// final walletService = Provider.of<WalletService>(context);
// final userService = Provider.of<UserService>(context);
// final priceStore = Provider.of<PriceStore>(context);
// final authenticationStore = Provider.of<AuthenticationStore>(context);
// final trades = Provider.of<Box<Trade>>(context);
// final transactionDescriptions =
// Provider.of<Box<TransactionDescription>>(context);
// final walletStore = Provider.of<WalletStore>(context);
// final settingsStore = Provider.of<SettingsStore>(context);
if (_isInactive && !_postFrameCallback) {
_postFrameCallback = true;
@@ -96,38 +103,51 @@ class RootState extends State<Root> with WidgetsBindingObserver {
}
return Observer(builder: (_) {
final state = _authenticationStore.state;
final state = widget.authenticationStore.state;
print(state);
if (state == AuthenticationState.denied) {
return createWelcomePage();
}
if (state == AuthenticationState.readyToLogin) {
return createLoginPage(
sharedPreferences: sharedPreferences,
userService: userService,
walletService: walletService,
walletListService: walletListService,
authenticationStore: authenticationStore);
if (state == AuthenticationState.installed) {
return getIt.get<AuthPage>();
}
if (state == AuthenticationState.authenticated ||
state == AuthenticationState.restored) {
return createDashboardPage(
walletService: walletService,
priceStore: priceStore,
trades: trades,
transactionDescriptions: transactionDescriptions,
walletStore: walletStore,
settingsStore: settingsStore);
if (state == AuthenticationState.allowed) {
return getIt.get<DashboardPage>();
}
if (state == AuthenticationState.created) {
return createSeedPage(
settingsStore: settingsStore,
walletService: walletService,
callback: () =>
_authenticationStore.state = AuthenticationState.authenticated);
}
// if (state == AuthenticationState.denied) {
// return createWelcomePage();
// }
// if (state == AuthenticationState.readyToLogin) {
// return createLoginPage(
// sharedPreferences: sharedPreferences,
// userService: userService,
// walletService: walletService,
// walletListService: walletListService,
// authenticationStore: authenticationStore);
// }
// if (state == AuthenticationState.authenticated ||
// state == AuthenticationState.restored) {
// return createDashboardPage(
// walletService: walletService,
// priceStore: priceStore,
// trades: trades,
// transactionDescriptions: transactionDescriptions,
// walletStore: walletStore,
// settingsStore: settingsStore);
// }
// if (state == AuthenticationState.created) {
// return createSeedPage(
// settingsStore: settingsStore,
// walletService: walletService,
// callback: () =>
// _authenticationStore.state = AuthenticationState.authenticated);
// }
return Container(color: Colors.white);
});
@@ -1,3 +1,4 @@
import 'package:cake_wallet/src/widgets/seed_language_selector.dart';
import 'package:provider/provider.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter/material.dart';
@@ -11,79 +12,68 @@ import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
class SeedLanguage extends BasePage {
SeedLanguage({this.onConfirm});
final Function(BuildContext, String) onConfirm;
@override
Widget body(BuildContext context) => SeedLanguageForm();
Widget body(BuildContext context) => SeedLanguageForm(onConfirm: onConfirm);
}
class SeedLanguageForm extends StatefulWidget {
SeedLanguageForm({this.onConfirm});
final Function(BuildContext, String) onConfirm;
@override
SeedLanguageFormState createState() => SeedLanguageFormState();
}
class SeedLanguageFormState extends State<SeedLanguageForm> {
static const aspectRatioImage = 1.22;
final walletNameImage = Image.asset('assets/images/wallet_name.png');
final _languageSelectorKey = GlobalKey<SeedLanguageSelectorState>();
@override
Widget build(BuildContext context) {
final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
final List<String> seedLocales = [
S.current.seed_language_english,
S.current.seed_language_chinese,
S.current.seed_language_dutch,
S.current.seed_language_german,
S.current.seed_language_japanese,
S.current.seed_language_portuguese,
S.current.seed_language_russian,
S.current.seed_language_spanish
];
return Container(
padding: EdgeInsets.only(top: 24),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
content: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(left: 12, right: 12),
child: AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(child: walletNameImage, fit: BoxFit.fill)),
),
Padding(padding: EdgeInsets.only(top: 40),
child: Text(
S.of(context).seed_language_choose,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color
),
),
),
Padding(padding: EdgeInsets.only(top: 24),
child: Observer(
builder: (_) => SelectButton(
image: null,
text: seedLocales[seedLanguages.indexOf(seedLanguageStore.selectedSeedLanguage)],
color: Theme.of(context).accentTextTheme.title.backgroundColor,
textColor: Theme.of(context).primaryTextTheme.title.color,
onTap: () async => await showDialog(
context: context,
builder: (BuildContext context) => SeedLanguagePicker()
)
)
),
)
]),
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
content:
Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
Padding(
padding: EdgeInsets.only(left: 12, right: 12),
child: AspectRatio(
aspectRatio: aspectRatioImage,
child: FittedBox(child: walletNameImage, fit: BoxFit.fill)),
),
Padding(
padding: EdgeInsets.only(top: 40),
child: Text(
S.of(context).seed_language_choose,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).primaryTextTheme.title.color),
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: SeedLanguageSelector(
key: _languageSelectorKey,
initialSelected: defaultSeedLanguage),
)
]),
bottomSectionPadding:
EdgeInsets.only(left: 24, right: 24, bottom: 24),
bottomSection: Observer(
builder: (context) {
return PrimaryButton(
onPressed: () =>
Navigator.of(context).popAndPushNamed(seedLanguageStore.currentRoute),
onPressed: () => widget
.onConfirm(context, _languageSelectorKey.currentState.selected),
text: S.of(context).seed_language_next,
color: Colors.green,
textColor: Colors.white);
@@ -17,7 +17,7 @@ List<Image> flagImages = [
Image.asset('assets/images/spain.png'),
];
List<String> languageCodes = [
const List<String> languageCodes = [
'Eng',
'Chi',
'Ned',
@@ -28,19 +28,39 @@ List<String> languageCodes = [
'Esp',
];
enum Places {topLeft, topRight, bottomLeft, bottomRight, inside}
const defaultSeedLanguage = 'English';
const List<String> seedLanguages = [
defaultSeedLanguage,
'Chinese (simplified)',
'Dutch',
'German',
'Japanese',
'Portuguese',
'Russian',
'Spanish'
];
enum Places { topLeft, topRight, bottomLeft, bottomRight, inside }
class SeedLanguagePicker extends StatefulWidget {
SeedLanguagePicker({Key key, this.selected = defaultSeedLanguage})
: super(key: key);
final String selected;
@override
SeedLanguagePickerState createState() => SeedLanguagePickerState();
SeedLanguagePickerState createState() =>
SeedLanguagePickerState(selected: selected);
}
class SeedLanguagePickerState extends State<SeedLanguagePicker> {
SeedLanguagePickerState({this.selected});
String selected;
@override
Widget build(BuildContext context) {
final seedLanguageStore = Provider.of<SeedLanguageStore>(context);
return GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
@@ -48,7 +68,8 @@ class SeedLanguagePickerState extends State<SeedLanguagePicker> {
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,
@@ -62,8 +83,7 @@ class SeedLanguagePickerState extends State<SeedLanguagePicker> {
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: Colors.white
),
color: Colors.white),
),
),
Padding(
@@ -74,9 +94,8 @@ class SeedLanguagePickerState extends State<SeedLanguagePicker> {
height: 300,
width: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(14)),
color: Theme.of(context).dividerColor
),
borderRadius: BorderRadius.all(Radius.circular(14)),
color: Theme.of(context).dividerColor),
child: GridView.count(
shrinkWrap: true,
crossAxisCount: 3,
@@ -85,71 +104,64 @@ class SeedLanguagePickerState extends State<SeedLanguagePicker> {
crossAxisSpacing: 1,
mainAxisSpacing: 1,
children: List.generate(9, (index) {
if (index == 8) {
return gridTile(
isCurrent: false,
place: Places.bottomRight,
image: null,
text: '',
onTap: null);
isCurrent: false,
place: Places.bottomRight,
image: null,
text: '',
onTap: null);
} else {
final code = languageCodes[index];
final flag = flagImages[index];
final isCurrent = index == seedLanguages.indexOf(seedLanguageStore.selectedSeedLanguage);
final isCurrent =
index == seedLanguages.indexOf(selected);
if (index == 0) {
return gridTile(
isCurrent: isCurrent,
place: Places.topLeft,
image: flag,
text: code,
onTap: () {
seedLanguageStore.setSelectedSeedLanguage(seedLanguages[index]);
Navigator.of(context).pop();
}
);
isCurrent: isCurrent,
place: Places.topLeft,
image: flag,
text: code,
onTap: () {
selected = seedLanguages[index];
Navigator.of(context).pop(selected);
});
}
if (index == 2) {
return gridTile(
isCurrent: isCurrent,
place: Places.topRight,
image: flag,
text: code,
onTap: () {
seedLanguageStore.setSelectedSeedLanguage(seedLanguages[index]);
Navigator.of(context).pop();
}
);
isCurrent: isCurrent,
place: Places.topRight,
image: flag,
text: code,
onTap: () {
selected = seedLanguages[index];
Navigator.of(context).pop(selected);
});
}
if (index == 6) {
return gridTile(
isCurrent: isCurrent,
place: Places.bottomLeft,
image: flag,
text: code,
onTap: () {
seedLanguageStore.setSelectedSeedLanguage(seedLanguages[index]);
Navigator.of(context).pop();
}
);
isCurrent: isCurrent,
place: Places.bottomLeft,
image: flag,
text: code,
onTap: () {
selected = seedLanguages[index];
Navigator.of(context).pop(selected);
});
}
return gridTile(
isCurrent: isCurrent,
place: Places.inside,
image: flag,
text: code,
onTap: () {
seedLanguageStore.setSelectedSeedLanguage(seedLanguages[index]);
Navigator.of(context).pop();
}
);
isCurrent: isCurrent,
place: Places.inside,
image: flag,
text: code,
onTap: () {
selected = seedLanguages[index];
Navigator.of(context).pop(selected);
});
}
}),
),
@@ -165,13 +177,12 @@ class SeedLanguagePickerState extends State<SeedLanguagePicker> {
);
}
Widget gridTile({
@required bool isCurrent,
@required Places place,
@required Image image,
@required String text,
@required VoidCallback onTap}) {
Widget gridTile(
{@required bool isCurrent,
@required Places place,
@required Image image,
@required String text,
@required VoidCallback onTap}) {
BorderRadius borderRadius;
final color = isCurrent
? Theme.of(context).accentTextTheme.subtitle.decorationColor
@@ -199,40 +210,33 @@ class SeedLanguagePickerState extends State<SeedLanguagePicker> {
}
return GestureDetector(
onTap: onTap,
child: Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: borderRadius,
color: color
),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
image != null
? image
: Offstage(),
Padding(
padding: image != null
? EdgeInsets.only(left: 10)
: EdgeInsets.only(left: 0),
child: Text(
text,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: textColor
onTap: onTap,
child: Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(borderRadius: borderRadius, color: color),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
image != null ? image : Offstage(),
Padding(
padding: image != null
? EdgeInsets.only(left: 10)
: EdgeInsets.only(left: 0),
child: Text(
text,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: textColor),
),
),
)
],
)
],
),
),
),
)
);
));
}
}
}
@@ -0,0 +1,75 @@
import 'package:mobx/mobx.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/view_model/address_list/address_edit_or_create_view_model.dart';
import 'package:cake_wallet/core/AddressLabelValidator.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';
class AddressEditOrCreatePage extends BasePage {
AddressEditOrCreatePage({@required this.addressEditOrCreateViewModel})
: _formKey = GlobalKey<FormState>(),
_labelController = TextEditingController(),
super() {
_labelController.addListener(
() => addressEditOrCreateViewModel.label = _labelController.text);
_labelController.text = addressEditOrCreateViewModel.label;
print(_labelController.text);
print(addressEditOrCreateViewModel.label);
}
final AddressEditOrCreateViewModel addressEditOrCreateViewModel;
final GlobalKey<FormState> _formKey;
final TextEditingController _labelController;
@override
String get title => S.current.new_subaddress_title;
@override
Widget body(BuildContext context) {
reaction((_) => addressEditOrCreateViewModel.state,
(AddressEditOrCreateState state) {
if (state is AddressSavedSuccessfully) {
WidgetsBinding.instance
.addPostFrameCallback((_) => Navigator.of(context).pop());
}
});
return Form(
key: _formKey,
child: Container(
padding: EdgeInsets.all(24.0),
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: BaseTextFormField(
controller: _labelController,
hintText: S.of(context).new_subaddress_label_name,
validator: AddressLabelValidator()))),
Observer(
builder: (_) => LoadingPrimaryButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
await addressEditOrCreateViewModel.save();
}
},
text: addressEditOrCreateViewModel.isEdit
? S.of(context).rename
: S.of(context).new_subaddress_create,
color: Colors.green,
textColor: Colors.white,
isLoading:
addressEditOrCreateViewModel.state is AddressIsSaving,
isDisabled:
addressEditOrCreateViewModel.label?.isEmpty ?? true,
),
)
],
),
));
}
}
@@ -1,130 +0,0 @@
import 'package:cake_wallet/src/domain/monero/subaddress.dart';
import 'package:mobx/mobx.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/generated/i18n.dart';
import 'package:cake_wallet/src/stores/subaddress_creation/subaddress_creation_state.dart';
import 'package:cake_wallet/src/stores/subaddress_creation/subaddress_creation_store.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
class NewSubaddressPage extends BasePage {
NewSubaddressPage({this.subaddress});
final Subaddress subaddress;
@override
String get title => S.current.new_subaddress_title;
@override
Widget body(BuildContext context) => NewSubaddressForm(subaddress);
@override
Widget build(BuildContext context) {
final subaddressCreationStore =
Provider.of<SubadrressCreationStore>(context);
reaction((_) => subaddressCreationStore.state, (SubaddressCreationState state) {
if (state is SubaddressCreatedSuccessfully) {
WidgetsBinding.instance
.addPostFrameCallback((_) => Navigator.of(context).pop());
}
});
return super.build(context);
}
}
class NewSubaddressForm extends StatefulWidget {
NewSubaddressForm(this.subaddress);
final Subaddress subaddress;
@override
NewSubaddressFormState createState() => NewSubaddressFormState(subaddress);
}
class NewSubaddressFormState extends State<NewSubaddressForm> {
NewSubaddressFormState(this.subaddress);
final _formKey = GlobalKey<FormState>();
final _labelController = TextEditingController();
final Subaddress subaddress;
@override
void initState() {
if (subaddress != null) _labelController.text = subaddress.label;
super.initState();
}
@override
void dispose() {
_labelController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final subaddressCreationStore =
Provider.of<SubadrressCreationStore>(context);
_labelController.addListener(() {
if (_labelController.text.isNotEmpty) {
subaddressCreationStore.setDisabledStatus(false);
} else {
subaddressCreationStore.setDisabledStatus(true);
}
});
return Form(
key: _formKey,
child: Container(
padding: EdgeInsets.all(24.0),
child: Column(
children: <Widget>[
Expanded(
child: Center(
child: BaseTextFormField(
controller: _labelController,
hintText: S.of(context).new_subaddress_label_name,
validator: (value) {
subaddressCreationStore.validateSubaddressName(value);
return subaddressCreationStore.errorMessage;
}
)
)
),
Observer(
builder: (_) => LoadingPrimaryButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
if (subaddress != null) {
await subaddressCreationStore.setLabel(
addressIndex: subaddress.id,
label: _labelController.text
);
} else {
await subaddressCreationStore.add(
label: _labelController.text);
}
}
},
text: subaddress != null
? S.of(context).rename
: S.of(context).new_subaddress_create,
color: Colors.green,
textColor: Colors.white,
isLoading:
subaddressCreationStore.state is SubaddressIsCreating,
isDisabled: subaddressCreationStore.isDisabledStatus,
),
)
],
),
)
);
}
}
@@ -32,9 +32,7 @@ class SubaddressListPage extends BasePage {
child: Observer(
builder: (_) => ListView.separated(
separatorBuilder: (_, __) => Divider(
color: Theme.of(context).dividerTheme.color,
height: 1.0,
),
color: Theme.of(context).dividerTheme.color, height: 1.0),
itemCount: subaddressListStore.subaddresses == null
? 0
: subaddressListStore.subaddresses.length,
@@ -42,9 +40,7 @@ class SubaddressListPage extends BasePage {
final subaddress = subaddressListStore.subaddresses[index];
final isCurrent =
walletStore.subaddress.address == subaddress.address;
final label = subaddress.label != null
? subaddress.label
: subaddress.address;
final label = subaddress.label ?? subaddress.address;
return InkWell(
onTap: () => Navigator.of(context).pop(subaddress),
-20
View File
@@ -1,20 +0,0 @@
abstract class AuthState {}
class AuthenticationStateInitial extends AuthState {}
class AuthenticationInProgress extends AuthState {}
class AuthenticatedSuccessfully extends AuthState {}
class AuthenticationFailure extends AuthState {
AuthenticationFailure({this.error});
final String error;
}
class AuthenticationBanned extends AuthState {
AuthenticationBanned({this.error});
final String error;
}
+99 -99
View File
@@ -1,99 +1,99 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/src/domain/services/user_service.dart';
import 'package:cake_wallet/src/domain/services/wallet_service.dart';
import 'package:cake_wallet/src/stores/auth/auth_state.dart';
import 'package:cake_wallet/generated/i18n.dart';
part 'auth_store.g.dart';
class AuthStore = AuthStoreBase with _$AuthStore;
abstract class AuthStoreBase with Store {
AuthStoreBase(
{@required this.userService,
@required this.walletService,
@required this.sharedPreferences}) {
state = AuthenticationStateInitial();
_failureCounter = 0;
}
static const maxFailedLogins = 3;
static const banTimeout = 180; // 3 mins
final banTimeoutKey = S.current.auth_store_ban_timeout;
final UserService userService;
final WalletService walletService;
final SharedPreferences sharedPreferences;
@observable
AuthState state;
@observable
int _failureCounter;
@action
Future auth({String password}) async {
state = AuthenticationStateInitial();
final _banDuration = banDuration();
if (_banDuration != null) {
state = AuthenticationBanned(
error: S.current.auth_store_banned_for + '${_banDuration.inMinutes}' + S.current.auth_store_banned_minutes);
return;
}
state = AuthenticationInProgress();
final isAuth = await userService.authenticate(password);
if (isAuth) {
state = AuthenticatedSuccessfully();
_failureCounter = 0;
} else {
_failureCounter += 1;
if (_failureCounter >= maxFailedLogins) {
final banDuration = await ban();
state = AuthenticationBanned(
error: S.current.auth_store_banned_for + '${banDuration.inMinutes}' + S.current.auth_store_banned_minutes);
return;
}
state = AuthenticationFailure(error: S.current.auth_store_incorrect_password);
}
}
Duration banDuration() {
final unbanTimestamp = sharedPreferences.getInt(banTimeoutKey);
if (unbanTimestamp == null) {
return null;
}
final unbanTime = DateTime.fromMillisecondsSinceEpoch(unbanTimestamp);
final now = DateTime.now();
if (now.isAfter(unbanTime)) {
return null;
}
return Duration(milliseconds: unbanTimestamp - now.millisecondsSinceEpoch);
}
Future<Duration> ban() async {
final multiplier = _failureCounter - maxFailedLogins + 1;
final timeout = (multiplier * banTimeout) * 1000;
final unbanTimestamp = DateTime.now().millisecondsSinceEpoch + timeout;
await sharedPreferences.setInt(banTimeoutKey, unbanTimestamp);
return Duration(milliseconds: timeout);
}
@action
void biometricAuth() {
state = AuthenticatedSuccessfully();
}
}
//import 'dart:async';
//import 'package:flutter/foundation.dart';
//import 'package:shared_preferences/shared_preferences.dart';
//import 'package:mobx/mobx.dart';
//import 'package:cake_wallet/src/domain/services/user_service.dart';
//import 'package:cake_wallet/src/domain/services/wallet_service.dart';
//import 'package:cake_wallet/view_model/auth_state.dart';
//import 'package:cake_wallet/generated/i18n.dart';
//
//part 'auth_store.g.dart';
//
//class AuthStore = AuthStoreBase with _$AuthStore;
//
//abstract class AuthStoreBase with Store {
// AuthStoreBase(
// {@required this.userService,
// @required this.walletService,
// @required this.sharedPreferences}) {
// state = AuthenticationStateInitial();
// _failureCounter = 0;
// }
//
// static const maxFailedLogins = 3;
// static const banTimeout = 180; // 3 mins
// final banTimeoutKey = S.current.auth_store_ban_timeout;
//
// final UserService userService;
// final WalletService walletService;
//
// final SharedPreferences sharedPreferences;
//
// @observable
// AuthState state;
//
// @observable
// int _failureCounter;
//
// @action
// Future auth({String password}) async {
// state = AuthenticationStateInitial();
// final _banDuration = banDuration();
//
// if (_banDuration != null) {
// state = AuthenticationBanned(
// error: S.current.auth_store_banned_for + '${_banDuration.inMinutes}' + S.current.auth_store_banned_minutes);
// return;
// }
//
// state = AuthenticationInProgress();
// final isAuth = await userService.authenticate(password);
//
// if (isAuth) {
// state = AuthenticatedSuccessfully();
// _failureCounter = 0;
// } else {
// _failureCounter += 1;
//
// if (_failureCounter >= maxFailedLogins) {
// final banDuration = await ban();
// state = AuthenticationBanned(
// error: S.current.auth_store_banned_for + '${banDuration.inMinutes}' + S.current.auth_store_banned_minutes);
// return;
// }
//
// state = AuthenticationFailure(error: S.current.auth_store_incorrect_password);
// }
// }
//
// Duration banDuration() {
// final unbanTimestamp = sharedPreferences.getInt(banTimeoutKey);
//
// if (unbanTimestamp == null) {
// return null;
// }
//
// final unbanTime = DateTime.fromMillisecondsSinceEpoch(unbanTimestamp);
// final now = DateTime.now();
//
// if (now.isAfter(unbanTime)) {
// return null;
// }
//
// return Duration(milliseconds: unbanTimestamp - now.millisecondsSinceEpoch);
// }
//
// Future<Duration> ban() async {
// final multiplier = _failureCounter - maxFailedLogins + 1;
// final timeout = (multiplier * banTimeout) * 1000;
// final unbanTimestamp = DateTime.now().millisecondsSinceEpoch + timeout;
// await sharedPreferences.setInt(banTimeoutKey, unbanTimestamp);
//
// return Duration(milliseconds: timeout);
// }
//
// @action
// void biometricAuth() {
// state = AuthenticatedSuccessfully();
// }
//}
@@ -30,8 +30,8 @@ abstract class AuthenticationStoreBase with Store {
@observable
AuthenticationState state;
@observable
String errorMessage;
// @observable
// String errorMessage;
Future started() async {
final canAuth = await userService.canAuthenticate();
@@ -2,7 +2,7 @@ import 'package:mobx/mobx.dart';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/src/domain/services/wallet_list_service.dart';
import 'package:cake_wallet/src/domain/common/mnemotic_item.dart';
import 'package:cake_wallet/src/domain/common/mnemonic_item.dart';
import 'package:cake_wallet/src/stores/wallet_restoration/wallet_restoration_state.dart';
import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
@@ -37,7 +37,7 @@ abstract class WalleRestorationStoreBase with Store {
bool isValid;
@observable
List<MnemoticItem> seed;
List<MnemonicItem> seed;
@observable
bool disabledState;
@@ -79,35 +79,35 @@ abstract class WalleRestorationStoreBase with Store {
}
@action
void setSeed(List<MnemoticItem> seed) {
void setSeed(List<MnemonicItem> seed) {
this.seed = seed;
}
@action
void validateSeed(List<MnemoticItem> seed) {
final _seed = seed != null ? seed : this.seed;
bool isValid = _seed != null ? _seed.length == 25 : false;
if (!isValid) {
errorMessage = S.current.wallet_restoration_store_incorrect_seed_length;
this.isValid = isValid;
return;
}
for (final item in _seed) {
if (!item.isCorrect()) {
isValid = false;
break;
}
}
if (isValid) {
errorMessage = null;
}
this.isValid = isValid;
return;
}
// @action
// void validateSeed(List<MnemonicItem> seed) {
// final _seed = seed != null ? seed : this.seed;
// bool isValid = _seed != null ? _seed.length == 25 : false;
//
// if (!isValid) {
// errorMessage = S.current.wallet_restoration_store_incorrect_seed_length;
// this.isValid = isValid;
// return;
// }
//
// for (final item in _seed) {
// if (!item.isCorrect()) {
// isValid = false;
// break;
// }
// }
//
// if (isValid) {
// errorMessage = null;
// }
//
// this.isValid = isValid;
// return;
// }
String _seedText() {
return seed.fold('', (acc, item) => acc + ' ' + item.toString());
+39 -43
View File
@@ -2,24 +2,24 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class BaseTextFormField extends StatelessWidget {
BaseTextFormField({
this.controller,
this.keyboardType = TextInputType.text,
this.textInputAction = TextInputAction.done,
this.textAlign = TextAlign.start,
this.autovalidate = false,
this.hintText = '',
this.maxLines = 1,
this.inputFormatters,
this.textColor,
this.hintColor,
this.borderColor,
this.prefix,
this.suffix,
this.suffixIcon,
this.enabled = true,
this.validator
});
BaseTextFormField(
{this.controller,
this.keyboardType = TextInputType.text,
this.textInputAction = TextInputAction.done,
this.textAlign = TextAlign.start,
this.autovalidate = false,
this.hintText = '',
this.maxLines = 1,
this.inputFormatters,
this.textColor,
this.hintColor,
this.borderColor,
this.prefix,
this.suffix,
this.suffixIcon,
this.enabled = true,
this.validator,
this.placeholderTextStyle});
final TextEditingController controller;
final TextInputType keyboardType;
@@ -37,6 +37,7 @@ class BaseTextFormField extends StatelessWidget {
final Widget suffixIcon;
final bool enabled;
final FormFieldValidator<String> validator;
final TextStyle placeholderTextStyle;
@override
Widget build(BuildContext context) {
@@ -50,32 +51,27 @@ class BaseTextFormField extends StatelessWidget {
inputFormatters: inputFormatters,
enabled: enabled,
style: TextStyle(
fontSize: 16.0,
color: textColor ?? Theme.of(context).primaryTextTheme.title.color
),
fontSize: 16.0,
color: textColor ?? Theme.of(context).primaryTextTheme.title.color),
decoration: InputDecoration(
prefix: prefix,
suffix: suffix,
suffixIcon: suffixIcon,
hintStyle: TextStyle(
color: hintColor ?? Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: hintText,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? Theme.of(context).dividerColor,
width: 1.0
)
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? Theme.of(context).dividerColor,
width: 1.0
)
)
),
prefix: prefix,
suffix: suffix,
suffixIcon: suffixIcon,
hintStyle: placeholderTextStyle ??
TextStyle(
color: hintColor ??
Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16),
hintText: hintText,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: borderColor ?? Theme.of(context).dividerColor,
width: 1.0))),
validator: validator,
);
}
}
}
+30 -16
View File
@@ -4,7 +4,10 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/monero/get_height_by_date.dart';
class BlockchainHeightWidget extends StatefulWidget {
BlockchainHeightWidget({GlobalKey key}) : super(key: key);
BlockchainHeightWidget({GlobalKey key, this.onHeightChange})
: super(key: key);
final Function(int) onHeightChange;
@override
State<StatefulWidget> createState() => BlockchainHeightState();
@@ -13,15 +16,23 @@ class BlockchainHeightWidget extends StatefulWidget {
class BlockchainHeightState extends State<BlockchainHeightWidget> {
final dateController = TextEditingController();
final restoreHeightController = TextEditingController();
int get height => _height;
int _height = 0;
@override
void initState() {
restoreHeightController.addListener(() => _height =
restoreHeightController.text != null
restoreHeightController.addListener(() {
try {
_changeHeight(restoreHeightController.text != null &&
restoreHeightController.text.isNotEmpty
? int.parse(restoreHeightController.text)
: 0);
} catch (_) {
_changeHeight(0);
}
});
super.initState();
}
@@ -38,21 +49,18 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
child: TextFormField(
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
color: Theme.of(context).primaryTextTheme.title.color),
controller: restoreHeightController,
keyboardType: TextInputType.numberWithOptions(
signed: false, decimal: false),
decoration: InputDecoration(
hintStyle: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
fontSize: 16),
hintText: S.of(context).widgets_restore_from_blockheight,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
color: Theme.of(context).dividerColor, width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
@@ -81,13 +89,14 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
child: TextFormField(
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
),
color: Theme.of(context).primaryTextTheme.title.color),
decoration: InputDecoration(
hintStyle: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
color: Theme.of(context)
.primaryTextTheme
.caption
.color,
fontSize: 16),
hintText: S.of(context).widgets_restore_from_date,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
@@ -113,7 +122,7 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
Future _selectDate(BuildContext context) async {
final now = DateTime.now();
final DateTime date = await showDatePicker(
final date = await showDatePicker(
context: context,
initialDate: now.subtract(Duration(days: 1)),
firstDate: DateTime(2014, DateTime.april),
@@ -125,8 +134,13 @@ class BlockchainHeightState extends State<BlockchainHeightWidget> {
setState(() {
dateController.text = DateFormat('yyyy-MM-dd').format(date);
restoreHeightController.text = '$height';
_height = height;
_changeHeight(height);
});
}
}
void _changeHeight(int height) {
_height = height;
widget.onHeightChange?.call(height);
}
}
@@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
class SeedLanguageSelector extends StatefulWidget {
SeedLanguageSelector({Key key, this.initialSelected}) : super(key: key);
final String initialSelected;
@override
SeedLanguageSelectorState createState() =>
SeedLanguageSelectorState(selected: initialSelected);
}
class SeedLanguageSelectorState extends State<SeedLanguageSelector> {
SeedLanguageSelectorState({this.selected});
final seedLocales = [
S.current.seed_language_english,
S.current.seed_language_chinese,
S.current.seed_language_dutch,
S.current.seed_language_german,
S.current.seed_language_japanese,
S.current.seed_language_portuguese,
S.current.seed_language_russian,
S.current.seed_language_spanish
];
String selected;
final _pickerKey = GlobalKey<SeedLanguagePickerState>();
@override
Widget build(BuildContext context) {
return SelectButton(
image: null,
text: seedLocales[seedLanguages.indexOf(selected)],
color: Theme.of(context).accentTextTheme.title.backgroundColor,
textColor: Theme.of(context).primaryTextTheme.title.color,
onTap: () async {
final selected = await showDialog<String>(
context: context,
builder: (BuildContext context) =>
SeedLanguagePicker(key: _pickerKey, selected: this.selected));
setState(() => this.selected = selected);
});
}
}
+235 -256
View File
@@ -1,94 +1,64 @@
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/domain/monero/mnemonics/english.dart';
import 'package:cake_wallet/src/domain/monero/mnemonics/chinese_simplified.dart';
import 'package:cake_wallet/src/domain/monero/mnemonics/dutch.dart';
import 'package:cake_wallet/src/domain/monero/mnemonics/german.dart';
import 'package:cake_wallet/src/domain/monero/mnemonics/japanese.dart';
import 'package:cake_wallet/src/domain/monero/mnemonics/portuguese.dart';
import 'package:cake_wallet/src/domain/monero/mnemonics/russian.dart';
import 'package:cake_wallet/src/domain/monero/mnemonics/spanish.dart';
import 'package:cake_wallet/src/domain/common/mnemotic_item.dart';
import 'package:cake_wallet/core/seed_validator.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/domain/common/mnemonic_item.dart';
import 'package:cake_wallet/generated/i18n.dart';
class SeedWidget extends StatefulWidget {
SeedWidget({Key key, this.onMnemoticChange, this.onFinish, this.seedLanguage}) : super(key: key) {
switch (seedLanguage) {
case 'English':
words = EnglishMnemonics.words;
break;
case 'Chinese (simplified)':
words = ChineseSimplifiedMnemonics.words;
break;
case 'Dutch':
words = DutchMnemonics.words;
break;
case 'German':
words = GermanMnemonics.words;
break;
case 'Japanese':
words = JapaneseMnemonics.words;
break;
case 'Portuguese':
words = PortugueseMnemonics.words;
break;
case 'Russian':
words = RussianMnemonics.words;
break;
case 'Spanish':
words = SpanishMnemonics.words;
break;
default:
words = EnglishMnemonics.words;
}
}
SeedWidget(
{Key key,
this.maxLength,
this.onMnemonicChange,
this.onFinish,
this.validator})
: super(key: key);
final Function(List<MnemoticItem>) onMnemoticChange;
final int maxLength;
final Function(List<MnemonicItem>) onMnemonicChange;
final Function() onFinish;
final String seedLanguage;
List<String> words;
final SeedValidator validator;
@override
SeedWidgetState createState() => SeedWidgetState();
SeedWidgetState createState() => SeedWidgetState(maxLength: maxLength);
}
class SeedWidgetState extends State<SeedWidget> {
static const maxLength = 25;
SeedWidgetState({this.maxLength});
List<MnemoticItem> items = <MnemoticItem>[];
List<MnemonicItem> items = <MnemonicItem>[];
final int maxLength;
final _seedController = TextEditingController();
final _seedTextFieldKey = GlobalKey();
MnemoticItem selectedItem;
MnemonicItem selectedItem;
bool isValid;
String errorMessage;
List<MnemoticItem> currentMnemotics;
bool isCurrentMnemoticValid;
List<MnemonicItem> currentMnemonics;
bool isCurrentMnemonicValid;
String _errorMessage;
@override
void initState() {
super.initState();
isValid = false;
isCurrentMnemoticValid = false;
isCurrentMnemonicValid = false;
_seedController
.addListener(() => changeCurrentMnemotic(_seedController.text));
.addListener(() => changeCurrentMnemonic(_seedController.text));
}
void addMnemotic(String text) {
setState(() => items.add(MnemoticItem(
text: text.trim().toLowerCase(), dic: widget.words)));
void addMnemonic(String text) {
setState(() => items.add(MnemonicItem(text: text.trim().toLowerCase())));
_seedController.text = '';
if (widget.onMnemoticChange != null) {
widget.onMnemoticChange(items);
if (widget.onMnemonicChange != null) {
widget.onMnemonicChange(items);
}
}
void mnemoticFromText(String text) {
void mnemonicFromText(String text) {
final splitted = text.split(' ');
if (splitted.length >= 2) {
@@ -98,18 +68,18 @@ class SeedWidgetState extends State<SeedWidget> {
}
if (selectedItem != null) {
editTextOfSelectedMnemotic(text);
editTextOfSelectedMnemonic(text);
} else {
addMnemotic(text);
addMnemonic(text);
}
}
}
}
void selectMnemotic(MnemoticItem item) {
void selectMnemonic(MnemonicItem item) {
setState(() {
selectedItem = item;
currentMnemotics = [item];
currentMnemonics = [item];
_seedController
..text = item.text
@@ -117,23 +87,23 @@ class SeedWidgetState extends State<SeedWidget> {
});
}
void onMnemoticTap(MnemoticItem item) {
void onMnemonicTap(MnemonicItem item) {
if (selectedItem == item) {
setState(() => selectedItem = null);
_seedController.text = '';
return;
}
selectMnemotic(item);
selectMnemonic(item);
}
void editTextOfSelectedMnemotic(String text) {
void editTextOfSelectedMnemonic(String text) {
setState(() => selectedItem.changeText(text));
selectedItem = null;
_seedController.text = '';
if (widget.onMnemoticChange != null) {
widget.onMnemoticChange(items);
if (widget.onMnemonicChange != null) {
widget.onMnemonicChange(items);
}
}
@@ -143,83 +113,77 @@ class SeedWidgetState extends State<SeedWidget> {
selectedItem = null;
_seedController.text = '';
if (widget.onMnemoticChange != null) {
widget.onMnemoticChange(items);
if (widget.onMnemonicChange != null) {
widget.onMnemonicChange(items);
}
});
}
void invalidate() {
setState(() => isValid = false);
}
void invalidate() => setState(() => isValid = false);
void validated() {
setState(() => isValid = true);
}
void validated() => setState(() => isValid = true);
void setErrorMessage(String errorMessage) {
setState(() => this.errorMessage = errorMessage);
}
void setErrorMessage(String errorMessage) =>
setState(() => this.errorMessage = errorMessage);
void replaceText(String text) {
setState(() => items = []);
mnemoticFromText(text);
mnemonicFromText(text);
}
void changeCurrentMnemotic(String text) {
void changeCurrentMnemonic(String text) {
setState(() {
final trimmedText = text.trim();
final splitted = trimmedText.split(' ');
_errorMessage = null;
if (text == null) {
currentMnemotics = [];
isCurrentMnemoticValid = false;
currentMnemonics = [];
isCurrentMnemonicValid = false;
return;
}
currentMnemotics = splitted
.map((text) => MnemoticItem(text: text, dic: widget.words))
.toList();
currentMnemonics =
splitted.map((text) => MnemonicItem(text: text)).toList();
bool isValid = true;
var isValid = true;
for (final word in currentMnemotics) {
isValid = word.isCorrect();
for (final word in currentMnemonics) {
isValid = widget.validator.isValid(word);
if (!isValid) {
break;
}
}
isCurrentMnemoticValid = isValid;
isCurrentMnemonicValid = isValid;
});
}
void saveCurrentMnemoticToItems() {
void saveCurrentMnemonicToItems() {
setState(() {
if (selectedItem != null) {
selectedItem.changeText(currentMnemotics.first.text.trim());
selectedItem.changeText(currentMnemonics.first.text.trim());
selectedItem = null;
} else {
items.addAll(currentMnemotics);
items.addAll(currentMnemonics);
}
currentMnemotics = [];
currentMnemonics = [];
_seedController.text = '';
});
}
void showErrorIfExist() {
setState(() => _errorMessage =
!isCurrentMnemoticValid ? S.current.incorrect_seed : null);
!isCurrentMnemonicValid ? S.current.incorrect_seed : null);
}
bool isSeedValid() {
bool isValid;
for (final item in items) {
isValid = item.isCorrect();
isValid = widget.validator.isValid(item);
if (!isValid) {
break;
@@ -234,192 +198,207 @@ class SeedWidgetState extends State<SeedWidget> {
return Container(
child: Column(children: [
Flexible(
fit: FlexFit.tight,
flex: 1,
child: Container(
width: double.infinity,
height: double.infinity,
padding: EdgeInsets.all(24),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24)
fit: FlexFit.tight,
flex: 1,
child: Container(
width: double.infinity,
height: double.infinity,
padding: EdgeInsets.all(24),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24)),
color: Theme.of(context).accentTextTheme.title.backgroundColor),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).restore_active_seed,
style: TextStyle(
fontSize: 14,
color:
Theme.of(context).primaryTextTheme.caption.color),
),
color: Theme.of(context).accentTextTheme.title.backgroundColor
),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).restore_active_seed,
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryTextTheme.caption.color
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Wrap(
children: items.map((item) {
final isValid = item.isCorrect();
final isSelected = selectedItem == item;
Padding(
padding: EdgeInsets.only(top: 5),
child: Wrap(
children: items.map((item) {
final isValid = widget.validator.isValid(item);
final isSelected = selectedItem == item;
return InkWell(
onTap: () => onMnemoticTap(item),
child: Container(
decoration: BoxDecoration(
color: isValid ? Colors.transparent : Palette.red),
margin: EdgeInsets.only(right: 7, bottom: 8),
child: Text(
item.toString(),
style: TextStyle(
color: isValid
? Theme.of(context).primaryTextTheme.title.color
: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16,
fontWeight:
isSelected ? FontWeight.w900 : FontWeight.w400,
decoration: isSelected
? TextDecoration.underline
: TextDecoration.none),
)),
);
}).toList(),)
)
],
),
return InkWell(
onTap: () => onMnemonicTap(item),
child: Container(
decoration: BoxDecoration(
color: isValid
? Colors.transparent
: Palette.red),
margin: EdgeInsets.only(right: 7, bottom: 8),
child: Text(
item.toString(),
style: TextStyle(
color: isValid
? Theme.of(context)
.primaryTextTheme
.title
.color
: Theme.of(context)
.primaryTextTheme
.caption
.color,
fontSize: 16,
fontWeight: isSelected
? FontWeight.w900
: FontWeight.w400,
decoration: isSelected
? TextDecoration.underline
: TextDecoration.none),
)),
);
}).toList(),
))
],
),
),
),
),
Flexible(
fit: FlexFit.tight,
flex: 2,
child: Padding(
padding: EdgeInsets.only(left: 24, top: 48, right: 24, bottom: 24),
padding:
EdgeInsets.only(left: 24, top: 48, right: 24, bottom: 24),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
S.of(context).restore_new_seed,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Theme.of(context).primaryTextTheme.title.color
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: TextFormField(
key: _seedTextFieldKey,
onFieldSubmitted: (text) => isCurrentMnemoticValid
? saveCurrentMnemoticToItems()
: null,
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
S.of(context).restore_new_seed,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color:
Theme.of(context).primaryTextTheme.title.color),
),
controller: _seedController,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
suffixIcon: GestureDetector(
behavior: HitTestBehavior.opaque,
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 145),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
'${items.length}/${SeedWidgetState.maxLength}',
style: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 14)),
SizedBox(width: 10),
InkWell(
onTap: () async =>
Clipboard.getData('text/plain').then(
Padding(
padding: EdgeInsets.only(top: 24),
child: TextFormField(
key: _seedTextFieldKey,
onFieldSubmitted: (text) => isCurrentMnemonicValid
? saveCurrentMnemonicToItems()
: null,
style: TextStyle(
fontSize: 16.0,
color:
Theme.of(context).primaryTextTheme.title.color),
controller: _seedController,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
suffixIcon: GestureDetector(
behavior: HitTestBehavior.opaque,
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 145),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text('${items.length}/$maxLength',
style: TextStyle(
color: Theme.of(context)
.primaryTextTheme
.caption
.color,
fontSize: 14)),
SizedBox(width: 10),
InkWell(
onTap: () async =>
Clipboard.getData('text/plain').then(
(clipboard) =>
replaceText(clipboard.text)),
child: Container(
height: 35,
padding: EdgeInsets.all(7),
decoration: BoxDecoration(
color:
Theme.of(context).accentTextTheme.title.backgroundColor,
borderRadius:
BorderRadius.circular(10.0)),
child: Text(
S.of(context).paste,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.title.color
),
)),
)
],
replaceText(clipboard.text)),
child: Container(
height: 35,
padding: EdgeInsets.all(7),
decoration: BoxDecoration(
color: Theme.of(context)
.accentTextTheme
.title
.backgroundColor,
borderRadius:
BorderRadius.circular(10.0)),
child: Text(
S.of(context).paste,
style: TextStyle(
color: Theme.of(context)
.primaryTextTheme
.title
.color),
)),
)
],
),
),
),
),
),
hintStyle:
TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontSize: 16
),
hintText: S.of(context).restore_from_seed_placeholder,
errorText: _errorMessage,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor, width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
enableInteractiveSelection: false,
),
)
]),
)
),
hintStyle: TextStyle(
color: Theme.of(context)
.primaryTextTheme
.caption
.color,
fontSize: 16),
hintText:
S.of(context).restore_from_seed_placeholder,
errorText: _errorMessage,
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))),
enableInteractiveSelection: false,
),
)
]),
)),
Padding(
padding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
child: Row(
children: <Widget>[
Flexible(
child: Padding(
padding: EdgeInsets.only(right: 8),
child: PrimaryButton(
onPressed: clear,
text: S.of(context).clear,
color: Colors.red,
textColor: Colors.white,
isDisabled: items.isEmpty,
),
)
),
child: Padding(
padding: EdgeInsets.only(right: 8),
child: PrimaryButton(
onPressed: clear,
text: S.of(context).clear,
color: Colors.red,
textColor: Colors.white,
isDisabled: items.isEmpty,
),
)),
Flexible(
child: Padding(
padding: EdgeInsets.only(left: 8),
child: (selectedItem == null && items.length == maxLength)
? PrimaryButton(
text: S.of(context).restore_next,
isDisabled: !isSeedValid(),
onPressed: () => widget.onFinish != null
? widget.onFinish()
: null,
color: Colors.green,
textColor: Colors.white)
text: S.of(context).restore_next,
isDisabled: !isSeedValid(),
onPressed: () => widget.onFinish != null
? widget.onFinish()
: null,
color: Colors.green,
textColor: Colors.white)
: PrimaryButton(
text: selectedItem != null
? S.of(context).save
: S.of(context).add_new_word,
onPressed: () => isCurrentMnemoticValid
? saveCurrentMnemoticToItems()
: null,
onDisabledPressed: () => showErrorIfExist(),
isDisabled: !isCurrentMnemoticValid,
color: Colors.green,
textColor: Colors.white),
text: selectedItem != null
? S.of(context).save
: S.of(context).add_new_word,
onPressed: () => isCurrentMnemonicValid
? saveCurrentMnemonicToItems()
: null,
onDisabledPressed: () => showErrorIfExist(),
isDisabled: !isCurrentMnemonicValid,
color: Colors.green,
textColor: Colors.white),
),
)
],