Fixes
M committed
Sep 29, 2020 at 20:56 UTC
696b808698a9b12f855f4340e38594f9a752bfab
10 files changed
+948
-846
lib/main.dart
+1
@@ -131,6 +131,7 @@ class App extends StatelessWidget {
131
return Observer(builder: (BuildContext context) {
132
return Root(
133
authenticationStore: authenticationStore,
134
+ navigatorKey: navigatorKey,
135
child: MaterialApp(
136
navigatorKey: navigatorKey,
137
debugShowCheckedModeBanner: false,
lib/src/screens/base_page.dart
+4
-4
@@ -37,7 +37,7 @@ abstract class BasePage extends StatelessWidget {
37
38
Widget Function(BuildContext, Widget) get rootWrapper => null;
39
40
- bool get _isDarkTheme => getIt.get<SettingsStore>().isDarkTheme;
40
+ bool get isDarkTheme => getIt.get<SettingsStore>().isDarkTheme;
41
42
void onOpenEndDrawer() => _scaffoldKey.currentState.openEndDrawer();
43
@@ -51,7 +51,7 @@ abstract class BasePage extends StatelessWidget {
51
final _backButton = Image.asset('assets/images/back_arrow.png',
52
color: titleColor ?? Theme.of(context).primaryTextTheme.title.color);
53
final _closeButton =
54
- _isDarkTheme ? _closeButtonImageDarkTheme : _closeButtonImage;
54
+ isDarkTheme ? _closeButtonImageDarkTheme : _closeButtonImage;
55
56
return SizedBox(
57
height: 37,
@@ -88,7 +88,7 @@ abstract class BasePage extends StatelessWidget {
88
89
ObstructingPreferredSizeWidget appBar(BuildContext context) {
90
final appBarColor =
91
- _isDarkTheme ? backgroundDarkColor : backgroundLightColor;
91
+ isDarkTheme ? backgroundDarkColor : backgroundLightColor;
92
93
switch (appBarStyle) {
94
case AppBarStyle.regular:
@@ -133,7 +133,7 @@ abstract class BasePage extends StatelessWidget {
133
final root = Scaffold(
134
key: _scaffoldKey,
135
backgroundColor:
136
- _isDarkTheme ? backgroundDarkColor : backgroundLightColor,
136
+ isDarkTheme ? backgroundDarkColor : backgroundLightColor,
137
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
138
extendBodyBehindAppBar: extendBodyBehindAppBar,
139
endDrawer: endDrawer,
lib/src/screens/exchange/exchange_page.dart
+339
-307
@@ -1,7 +1,9 @@
1
import 'dart:ui';
2
+import 'package:cake_wallet/core/execution_state.dart';
3
import 'package:cake_wallet/exchange/exchange_template.dart';
4
import 'package:cake_wallet/src/screens/base_page.dart';
5
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
6
+import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
7
import 'package:cake_wallet/src/widgets/template_tile.dart';
8
import 'package:cake_wallet/src/widgets/trail_button.dart';
9
import 'package:cake_wallet/utils/show_pop_up.dart';
@@ -9,11 +11,13 @@ import 'package:dotted_border/dotted_border.dart';
11
import 'package:flutter/cupertino.dart';
12
import 'package:flutter/material.dart';
13
import 'package:flutter_mobx/flutter_mobx.dart';
14
+import 'package:keyboard_actions/keyboard_actions.dart';
15
import 'package:mobx/mobx.dart';
16
import 'package:cake_wallet/routes.dart';
17
import 'package:cake_wallet/generated/i18n.dart';
18
import 'package:cake_wallet/entities/crypto_currency.dart';
19
import 'package:cake_wallet/exchange/xmrto/xmrto_exchange_provider.dart';
20
+
21
// import 'package:cake_wallet/exchange/exchange_trade_state.dart';
22
// import 'package:cake_wallet/exchange/limits_state.dart';
23
import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
@@ -32,6 +36,8 @@ class ExchangePage extends BasePage {
36
final depositKey = GlobalKey<ExchangeCardState>();
37
final receiveKey = GlobalKey<ExchangeCardState>();
38
final _formKey = GlobalKey<FormState>();
39
+ final _depositAmountFocus = FocusNode();
40
+ final _receiveAmountFocus = FocusNode();
41
var _isReactionsSet = false;
42
43
@override
@@ -54,11 +60,8 @@ class ExchangePage extends BasePage {
60
PresentProviderPicker(exchangeViewModel: exchangeViewModel);
61
62
@override
57
- Widget trailing(BuildContext context) =>
58
- TrailButton(
59
- caption: S.of(context).reset,
60
- onPressed: () => exchangeViewModel.reset()
61
- );
63
+ Widget trailing(BuildContext context) => TrailButton(
64
+ caption: S.of(context).reset, onPressed: () => exchangeViewModel.reset());
65
66
@override
67
Widget body(BuildContext context) {
@@ -74,305 +77,334 @@ class ExchangePage extends BasePage {
77
);
78
79
final depositWalletName =
77
- exchangeViewModel.depositCurrency == CryptoCurrency.xmr
78
- ? exchangeViewModel.wallet.name
79
- : null;
80
+ exchangeViewModel.depositCurrency == CryptoCurrency.xmr
81
+ ? exchangeViewModel.wallet.name
82
+ : null;
83
final receiveWalletName =
81
- exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
82
- ? exchangeViewModel.wallet.name
83
- : null;
84
+ exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
85
+ ? exchangeViewModel.wallet.name
86
+ : null;
87
88
WidgetsBinding.instance
89
.addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
90
88
- return Container(
89
- color: Theme.of(context).backgroundColor,
90
- child: Form(
91
- key: _formKey,
92
- child: ScrollableWithBottomSection(
93
- contentPadding: EdgeInsets.only(bottom: 24),
94
- content: Column(
95
- children: <Widget>[
96
- Container(
97
- padding: EdgeInsets.only(bottom: 32),
98
- decoration: BoxDecoration(
99
- borderRadius: BorderRadius.only(
100
- bottomLeft: Radius.circular(24),
101
- bottomRight: Radius.circular(24)
91
+ return KeyboardActions(
92
+ config: KeyboardActionsConfig(
93
+ keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
94
+ keyboardBarColor: isDarkTheme
95
+ ? Color.fromRGBO(48, 51, 60, 1.0)
96
+ : Color.fromRGBO(98, 98, 98, 1.0),
97
+ nextFocus: false,
98
+ actions: [
99
+ KeyboardActionsItem(
100
+ focusNode: _depositAmountFocus,
101
+ toolbarButtons: [(_) => KeyboardDoneButton()]),
102
+ KeyboardActionsItem(
103
+ focusNode: _receiveAmountFocus,
104
+ toolbarButtons: [(_) => KeyboardDoneButton()])
105
+ ]),
106
+ child: Container(
107
+ height: 1,
108
+ color: Theme.of(context).backgroundColor,
109
+ child: Form(
110
+ key: _formKey,
111
+ child: ScrollableWithBottomSection(
112
+ contentPadding: EdgeInsets.only(bottom: 24),
113
+ content: Column(
114
+ children: <Widget>[
115
+ Container(
116
+ padding: EdgeInsets.only(bottom: 32),
117
+ decoration: BoxDecoration(
118
+ borderRadius: BorderRadius.only(
119
+ bottomLeft: Radius.circular(24),
120
+ bottomRight: Radius.circular(24)),
121
+ gradient: LinearGradient(
122
+ colors: [
123
+ Theme.of(context).primaryTextTheme.body1.color,
124
+ Theme.of(context)
125
+ .primaryTextTheme
126
+ .body1
127
+ .decorationColor,
128
+ ],
129
+ stops: [
130
+ 0.35,
131
+ 1.0
132
+ ],
133
+ begin: Alignment.topLeft,
134
+ end: Alignment.bottomRight),
135
),
103
- gradient: LinearGradient(
104
- colors: [
105
- Theme.of(context).primaryTextTheme.body1.color,
106
- Theme.of(context).primaryTextTheme.body1.decorationColor,
107
- ],
108
- stops: [0.35, 1.0],
109
- begin: Alignment.topLeft,
110
- end: Alignment.bottomRight),
111
- ),
112
- child: Column(
113
- children: <Widget>[
114
- Container(
115
- decoration: BoxDecoration(
116
- borderRadius: BorderRadius.only(
117
- bottomLeft: Radius.circular(24),
118
- bottomRight: Radius.circular(24)
119
- ),
120
- gradient: LinearGradient(
121
- colors: [
122
- Theme.of(context)
136
+ child: Column(
137
+ children: <Widget>[
138
+ Container(
139
+ decoration: BoxDecoration(
140
+ borderRadius: BorderRadius.only(
141
+ bottomLeft: Radius.circular(24),
142
+ bottomRight: Radius.circular(24)),
143
+ gradient: LinearGradient(
144
+ colors: [
145
+ Theme.of(context)
146
+ .primaryTextTheme
147
+ .subtitle
148
+ .color,
149
+ Theme.of(context)
150
+ .primaryTextTheme
151
+ .subtitle
152
+ .decorationColor,
153
+ ],
154
+ begin: Alignment.topLeft,
155
+ end: Alignment.bottomRight),
156
+ ),
157
+ padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
158
+ child: Observer(
159
+ builder: (_) => ExchangeCard(
160
+ amountFocusNode: _depositAmountFocus,
161
+ key: depositKey,
162
+ title: S.of(context).you_will_send,
163
+ initialCurrency:
164
+ exchangeViewModel.depositCurrency,
165
+ initialWalletName: depositWalletName,
166
+ initialAddress:
167
+ exchangeViewModel.depositCurrency ==
168
+ exchangeViewModel.wallet.currency
169
+ ? exchangeViewModel.wallet.address
170
+ : exchangeViewModel.depositAddress,
171
+ initialIsAmountEditable: true,
172
+ initialIsAddressEditable:
173
+ exchangeViewModel.isDepositAddressEnabled,
174
+ isAmountEstimated: false,
175
+ currencies: CryptoCurrency.all,
176
+ onCurrencySelected: (currency) =>
177
+ exchangeViewModel.changeDepositCurrency(
178
+ currency: currency),
179
+ imageArrow: arrowBottomPurple,
180
+ currencyButtonColor: Colors.transparent,
181
+ addressButtonsColor:
182
+ Theme.of(context).focusColor,
183
+ borderColor: Theme.of(context)
184
.primaryTextTheme
124
- .subtitle
185
+ .body2
186
.color,
126
- Theme.of(context)
127
- .primaryTextTheme
128
- .subtitle
129
- .decorationColor,
130
- ],
131
- begin: Alignment.topLeft,
132
- end: Alignment.bottomRight),
133
- ),
134
- padding: EdgeInsets.fromLTRB(24, 90, 24, 32),
135
- child: Observer(
136
- builder: (_) => ExchangeCard(
137
- key: depositKey,
138
- title: S.of(context).you_will_send,
139
- initialCurrency:
140
- exchangeViewModel.depositCurrency,
141
- initialWalletName: depositWalletName,
142
- initialAddress: exchangeViewModel
143
- .depositCurrency ==
144
- exchangeViewModel.wallet.currency
145
- ? exchangeViewModel.wallet.address
146
- : exchangeViewModel.depositAddress,
147
- initialIsAmountEditable: true,
148
- initialIsAddressEditable: exchangeViewModel
149
- .isDepositAddressEnabled,
150
- isAmountEstimated: false,
151
- currencies: CryptoCurrency.all,
152
- onCurrencySelected: (currency) =>
153
- exchangeViewModel.changeDepositCurrency(
154
- currency: currency),
155
- imageArrow: arrowBottomPurple,
156
- currencyButtonColor: Colors.transparent,
157
- addressButtonsColor:
158
- Theme.of(context).focusColor,
159
- borderColor: Theme.of(context)
160
- .primaryTextTheme
161
- .body2
162
- .color,
163
- currencyValueValidator: AmountValidator(
164
- type: exchangeViewModel.wallet.type),
165
- addressTextFieldValidator: AddressValidator(
166
- type:
167
- exchangeViewModel.depositCurrency),
187
+ currencyValueValidator: AmountValidator(
188
+ type: exchangeViewModel.wallet.type),
189
+ addressTextFieldValidator: AddressValidator(
190
+ type: exchangeViewModel.depositCurrency),
191
+ ),
192
+ ),
193
),
169
- ),
194
+ Padding(
195
+ padding:
196
+ EdgeInsets.only(top: 29, left: 24, right: 24),
197
+ child: Observer(
198
+ builder: (_) => ExchangeCard(
199
+ amountFocusNode: _receiveAmountFocus,
200
+ key: receiveKey,
201
+ title: S.of(context).you_will_get,
202
+ initialCurrency:
203
+ exchangeViewModel.receiveCurrency,
204
+ initialWalletName: receiveWalletName,
205
+ initialAddress: exchangeViewModel
206
+ .receiveCurrency ==
207
+ exchangeViewModel.wallet.currency
208
+ ? exchangeViewModel.wallet.address
209
+ : exchangeViewModel.receiveAddress,
210
+ initialIsAmountEditable: false,
211
+ initialIsAddressEditable:
212
+ exchangeViewModel
213
+ .isReceiveAddressEnabled,
214
+ isAmountEstimated: true,
215
+ currencies: CryptoCurrency.all,
216
+ onCurrencySelected: (currency) =>
217
+ exchangeViewModel
218
+ .changeReceiveCurrency(
219
+ currency: currency),
220
+ imageArrow: arrowBottomCakeGreen,
221
+ currencyButtonColor: Colors.transparent,
222
+ addressButtonsColor:
223
+ Theme.of(context).focusColor,
224
+ borderColor: Theme.of(context)
225
+ .primaryTextTheme
226
+ .body2
227
+ .decorationColor,
228
+ currencyValueValidator: AmountValidator(
229
+ type: exchangeViewModel.wallet.type),
230
+ addressTextFieldValidator:
231
+ AddressValidator(
232
+ type: exchangeViewModel
233
+ .receiveCurrency),
234
+ )),
235
+ )
236
+ ],
237
+ ),
238
+ ),
239
+ Padding(
240
+ padding: EdgeInsets.only(top: 30, left: 24, bottom: 24),
241
+ child: Row(
242
+ mainAxisAlignment: MainAxisAlignment.start,
243
+ children: <Widget>[
244
+ Text(
245
+ S.of(context).send_templates,
246
+ style: TextStyle(
247
+ fontSize: 18,
248
+ fontWeight: FontWeight.w600,
249
+ color: Theme.of(context)
250
+ .primaryTextTheme
251
+ .display4
252
+ .color),
253
+ )
254
+ ],
255
),
171
- Padding(
172
- padding: EdgeInsets.only(top: 29, left: 24, right: 24),
173
- child: Observer(
174
- builder: (_) => ExchangeCard(
175
- key: receiveKey,
176
- title: S.of(context).you_will_get,
177
- initialCurrency:
178
- exchangeViewModel.receiveCurrency,
179
- initialWalletName: receiveWalletName,
180
- initialAddress:
181
- exchangeViewModel.receiveCurrency ==
182
- exchangeViewModel.wallet.currency
183
- ? exchangeViewModel.wallet.address
184
- : exchangeViewModel.receiveAddress,
185
- initialIsAmountEditable: false,
186
- initialIsAddressEditable:
187
- exchangeViewModel.isReceiveAddressEnabled,
188
- isAmountEstimated: true,
189
- currencies: CryptoCurrency.all,
190
- onCurrencySelected: (currency) =>
191
- exchangeViewModel.changeReceiveCurrency(
192
- currency: currency),
193
- imageArrow: arrowBottomCakeGreen,
194
- currencyButtonColor: Colors.transparent,
195
- addressButtonsColor:
196
- Theme.of(context).focusColor,
197
- borderColor: Theme.of(context)
256
+ ),
257
+ Container(
258
+ height: 40,
259
+ width: double.infinity,
260
+ padding: EdgeInsets.only(left: 24),
261
+ child: SingleChildScrollView(
262
+ scrollDirection: Axis.horizontal,
263
+ child: Row(
264
+ children: <Widget>[
265
+ GestureDetector(
266
+ onTap: () => Navigator.of(context)
267
+ .pushNamed(Routes.exchangeTemplate),
268
+ child: Container(
269
+ padding:
270
+ EdgeInsets.only(left: 1, right: 10),
271
+ child: DottedBorder(
272
+ borderType: BorderType.RRect,
273
+ dashPattern: [6, 4],
274
+ color: Theme.of(context)
275
+ .primaryTextTheme
276
+ .display2
277
+ .decorationColor,
278
+ strokeWidth: 2,
279
+ radius: Radius.circular(20),
280
+ child: Container(
281
+ height: 34,
282
+ width: 75,
283
+ padding: EdgeInsets.only(
284
+ left: 10, right: 10),
285
+ alignment: Alignment.center,
286
+ decoration: BoxDecoration(
287
+ borderRadius: BorderRadius.all(
288
+ Radius.circular(20)),
289
+ color: Colors.transparent,
290
+ ),
291
+ child: Text(
292
+ S.of(context).send_new,
293
+ style: TextStyle(
294
+ fontSize: 14,
295
+ fontWeight: FontWeight.w600,
296
+ color: Theme.of(context)
297
+ .primaryTextTheme
298
+ .display3
299
+ .color),
300
+ ),
301
+ )),
302
+ ),
303
+ ),
304
+ Observer(builder: (_) {
305
+ final templates = exchangeViewModel.templates;
306
+ final itemCount =
307
+ exchangeViewModel.templates.length;
308
+
309
+ return ListView.builder(
310
+ scrollDirection: Axis.horizontal,
311
+ shrinkWrap: true,
312
+ physics: NeverScrollableScrollPhysics(),
313
+ itemCount: itemCount,
314
+ itemBuilder: (context, index) {
315
+ final template = templates[index];
316
+
317
+ return TemplateTile(
318
+ key: UniqueKey(),
319
+ amount: template.amount,
320
+ from: template.depositCurrency,
321
+ to: template.receiveCurrency,
322
+ onTap: () {
323
+ applyTemplate(
324
+ exchangeViewModel, template);
325
+ },
326
+ onRemove: () {
327
+ showPopUp<void>(
328
+ context: context,
329
+ builder: (dialogContext) {
330
+ return AlertWithTwoActions(
331
+ alertTitle: S
332
+ .of(context)
333
+ .template,
334
+ alertContent: S
335
+ .of(context)
336
+ .confirm_delete_template,
337
+ leftButtonText:
338
+ S.of(context).delete,
339
+ rightButtonText:
340
+ S.of(context).cancel,
341
+ actionLeftButton: () {
342
+ Navigator.of(
343
+ dialogContext)
344
+ .pop();
345
+ exchangeViewModel
346
+ .exchangeTemplateStore
347
+ .remove(
348
+ template:
349
+ template);
350
+ exchangeViewModel
351
+ .exchangeTemplateStore
352
+ .update();
353
+ },
354
+ actionRightButton: () =>
355
+ Navigator.of(
356
+ dialogContext)
357
+ .pop());
358
+ });
359
+ },
360
+ );
361
+ });
362
+ }),
363
+ ],
364
+ )))
365
+ ],
366
+ ),
367
+ bottomSectionPadding:
368
+ EdgeInsets.only(left: 24, right: 24, bottom: 24),
369
+ bottomSection: Column(children: <Widget>[
370
+ Padding(
371
+ padding: EdgeInsets.only(bottom: 15),
372
+ child: Observer(builder: (_) {
373
+ final description =
374
+ exchangeViewModel.provider is XMRTOExchangeProvider
375
+ ? S.of(context).amount_is_guaranteed
376
+ : S.of(context).amount_is_estimate;
377
+ return Center(
378
+ child: Text(
379
+ description,
380
+ style: TextStyle(
381
+ color: Theme.of(context)
382
.primaryTextTheme
199
- .body2
383
+ .display4
384
.decorationColor,
201
- currencyValueValidator: AmountValidator(
202
- type: exchangeViewModel.wallet.type),
203
- addressTextFieldValidator: AddressValidator(
204
- type: exchangeViewModel.receiveCurrency),
205
- )),
206
- )
207
- ],
208
- ),
209
- ),
210
- Padding(
211
- padding: EdgeInsets.only(top: 30, left: 24, bottom: 24),
212
- child: Row(
213
- mainAxisAlignment: MainAxisAlignment.start,
214
- children: <Widget>[
215
- Text(
216
- S.of(context).send_templates,
217
- style: TextStyle(
218
- fontSize: 18,
219
- fontWeight: FontWeight.w600,
220
- color: Theme.of(context)
221
- .primaryTextTheme
222
- .display4
223
- .color),
224
- )
225
- ],
385
+ fontWeight: FontWeight.w500,
386
+ fontSize: 12),
387
+ ),
388
+ );
389
+ }),
390
),
227
- ),
228
- Container(
229
- height: 40,
230
- width: double.infinity,
231
- padding: EdgeInsets.only(left: 24),
232
- child: SingleChildScrollView(
233
- scrollDirection: Axis.horizontal,
234
- child: Row(
235
- children: <Widget>[
236
- GestureDetector(
237
- onTap: () => Navigator.of(context)
238
- .pushNamed(Routes.exchangeTemplate),
239
- child: Container(
240
- padding: EdgeInsets.only(left: 1, right: 10),
241
- child: DottedBorder(
242
- borderType: BorderType.RRect,
243
- dashPattern: [6, 4],
244
- color: Theme.of(context)
245
- .primaryTextTheme
246
- .display2
247
- .decorationColor,
248
- strokeWidth: 2,
249
- radius: Radius.circular(20),
250
- child: Container(
251
- height: 34,
252
- width: 75,
253
- padding: EdgeInsets.only(
254
- left: 10, right: 10),
255
- alignment: Alignment.center,
256
- decoration: BoxDecoration(
257
- borderRadius: BorderRadius.all(
258
- Radius.circular(20)),
259
- color: Colors.transparent,
260
- ),
261
- child: Text(
262
- S.of(context).send_new,
263
- style: TextStyle(
264
- fontSize: 14,
265
- fontWeight: FontWeight.w600,
266
- color: Theme.of(context)
267
- .primaryTextTheme
268
- .display3
269
- .color),
270
- ),
271
- )),
272
- ),
273
- ),
274
- Observer(builder: (_) {
275
- final templates = exchangeViewModel.templates;
276
- final itemCount =
277
- exchangeViewModel.templates.length;
278
-
279
- return ListView.builder(
280
- scrollDirection: Axis.horizontal,
281
- shrinkWrap: true,
282
- physics: NeverScrollableScrollPhysics(),
283
- itemCount: itemCount,
284
- itemBuilder: (context, index) {
285
- final template = templates[index];
286
-
287
- return TemplateTile(
288
- key: UniqueKey(),
289
- amount: template.amount,
290
- from: template.depositCurrency,
291
- to: template.receiveCurrency,
292
- onTap: () {
293
- applyTemplate(
294
- exchangeViewModel, template);
295
- },
296
- onRemove: () {
297
- showPopUp<void>(
298
- context: context,
299
- builder: (dialogContext) {
300
- return AlertWithTwoActions(
301
- alertTitle:
302
- S.of(context).template,
303
- alertContent: S
304
- .of(context)
305
- .confirm_delete_template,
306
- leftButtonText:
307
- S.of(context).delete,
308
- rightButtonText:
309
- S.of(context).cancel,
310
- actionLeftButton: () {
311
- Navigator.of(
312
- dialogContext)
313
- .pop();
314
- exchangeViewModel
315
- .exchangeTemplateStore
316
- .remove(
317
- template:
318
- template);
319
- exchangeViewModel
320
- .exchangeTemplateStore
321
- .update();
322
- },
323
- actionRightButton: () =>
324
- Navigator.of(
325
- dialogContext)
326
- .pop());
327
- });
328
- },
329
- );
330
- });
331
- }),
332
- ],
333
- )))
334
- ],
335
- ),
336
- bottomSectionPadding:
337
- EdgeInsets.only(left: 24, right: 24, bottom: 24),
338
- bottomSection: Column(children: <Widget>[
339
- Padding(
340
- padding: EdgeInsets.only(bottom: 15),
341
- child: Observer(builder: (_) {
342
- final description =
343
- exchangeViewModel.provider is XMRTOExchangeProvider
344
- ? S.of(context).amount_is_guaranteed
345
- : S.of(context).amount_is_estimate;
346
- return Center(
347
- child: Text(
348
- description,
349
- style: TextStyle(
350
- color: Theme.of(context)
351
- .primaryTextTheme
352
- .display4
353
- .decorationColor,
354
- fontWeight: FontWeight.w500,
355
- fontSize: 12),
356
- ),
357
- );
358
- }),
359
- ),
360
- Observer(
361
- builder: (_) => LoadingPrimaryButton(
362
- text: S.of(context).exchange,
363
- onPressed: () {
364
- if (_formKey.currentState.validate()) {
365
- exchangeViewModel.createTrade();
366
- }
367
- },
368
- color: Theme.of(context).accentTextTheme.body2.color,
369
- textColor: Colors.white,
370
- isLoading:
371
- false, // FIXME: FIXME exchangeViewModel.tradeState is TradeIsCreating,
372
- )),
373
- ]),
374
- )),
375
- );
391
+ Observer(
392
+ builder: (_) => LoadingPrimaryButton(
393
+ text: S.of(context).exchange,
394
+ onPressed: () {
395
+ if (_formKey.currentState.validate()) {
396
+ exchangeViewModel.createTrade();
397
+ }
398
+ },
399
+ color:
400
+ Theme.of(context).accentTextTheme.body2.color,
401
+ textColor: Colors.white,
402
+ isLoading: exchangeViewModel.tradeState
403
+ is IsExecutingState,
404
+ )),
405
+ ]),
406
+ )),
407
+ ));
408
}
409
410
void applyTemplate(
@@ -435,23 +467,23 @@ class ExchangePage extends BasePage {
467
exchangeViewModel.depositCurrency, exchangeViewModel, depositKey);
468
469
reaction(
438
- (_) => exchangeViewModel.wallet.name,
439
- (String _) => _onWalletNameChange(
470
+ (_) => exchangeViewModel.wallet.name,
471
+ (String _) => _onWalletNameChange(
472
exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey));
473
474
reaction(
443
- (_) => exchangeViewModel.wallet.name,
444
- (String _) => _onWalletNameChange(
475
+ (_) => exchangeViewModel.wallet.name,
476
+ (String _) => _onWalletNameChange(
477
exchangeViewModel, exchangeViewModel.depositCurrency, depositKey));
478
479
reaction(
448
- (_) => exchangeViewModel.receiveCurrency,
449
- (CryptoCurrency currency) =>
480
+ (_) => exchangeViewModel.receiveCurrency,
481
+ (CryptoCurrency currency) =>
482
_onCurrencyChange(currency, exchangeViewModel, receiveKey));
483
484
reaction(
453
- (_) => exchangeViewModel.depositCurrency,
454
- (CryptoCurrency currency) =>
485
+ (_) => exchangeViewModel.depositCurrency,
486
+ (CryptoCurrency currency) =>
487
_onCurrencyChange(currency, exchangeViewModel, depositKey));
488
489
reaction((_) => exchangeViewModel.depositAmount, (String amount) {
@@ -467,9 +499,9 @@ class ExchangePage extends BasePage {
499
});
500
501
reaction((_) => exchangeViewModel.isDepositAddressEnabled,
470
- (bool isEnabled) {
471
- depositKey.currentState.isAddressEditable(isEditable: isEnabled);
472
- });
502
+ (bool isEnabled) {
503
+ depositKey.currentState.isAddressEditable(isEditable: isEnabled);
504
+ });
505
506
reaction((_) => exchangeViewModel.receiveAmount, (String amount) {
507
if (receiveKey.currentState.amountController.text != amount) {
@@ -484,9 +516,9 @@ class ExchangePage extends BasePage {
516
});
517
518
reaction((_) => exchangeViewModel.isReceiveAddressEnabled,
487
- (bool isEnabled) {
488
- receiveKey.currentState.isAddressEditable(isEditable: isEnabled);
489
- });
519
+ (bool isEnabled) {
520
+ receiveKey.currentState.isAddressEditable(isEditable: isEnabled);
521
+ });
522
523
// FIXME: FIXME
524
@@ -535,7 +567,7 @@ class ExchangePage extends BasePage {
567
// });
568
569
depositAddressController.addListener(
538
- () => exchangeViewModel.depositAddress = depositAddressController.text);
570
+ () => exchangeViewModel.depositAddress = depositAddressController.text);
571
572
depositAmountController.addListener(() {
573
if (depositAmountController.text != exchangeViewModel.depositAmount) {
@@ -545,7 +577,7 @@ class ExchangePage extends BasePage {
577
});
578
579
receiveAddressController.addListener(
548
- () => exchangeViewModel.receiveAddress = receiveAddressController.text);
580
+ () => exchangeViewModel.receiveAddress = receiveAddressController.text);
581
582
receiveAmountController.addListener(() {
583
if (receiveAmountController.text != exchangeViewModel.receiveAmount) {
lib/src/screens/exchange/widgets/exchange_card.dart
+160
-158
@@ -24,7 +24,8 @@ class ExchangeCard extends StatefulWidget {
24
this.addressButtonsColor = Colors.transparent,
25
this.borderColor = Colors.transparent,
26
this.currencyValueValidator,
27
- this.addressTextFieldValidator})
27
+ this.addressTextFieldValidator,
28
+ this.amountFocusNode})
29
: super(key: key);
30
31
final List<CryptoCurrency> currencies;
@@ -42,6 +43,7 @@ class ExchangeCard extends StatefulWidget {
43
final Color borderColor;
44
final FormFieldValidator<String> currencyValueValidator;
45
final FormFieldValidator<String> addressTextFieldValidator;
46
+ final FocusNode amountFocusNode;
47
48
@override
49
ExchangeCardState createState() => ExchangeCardState();
@@ -114,191 +116,191 @@ class ExchangeCardState extends State<ExchangeCard> {
116
@override
117
Widget build(BuildContext context) {
118
final copyImage = Image.asset('assets/images/copy_content.png',
117
- height: 16, width: 16,
119
+ height: 16,
120
+ width: 16,
121
color: Theme.of(context).primaryTextTheme.display2.color);
122
123
return Container(
124
width: double.infinity,
125
color: Colors.transparent,
123
- child: Column(
124
- crossAxisAlignment: CrossAxisAlignment.start,
125
- children: <Widget>[
126
+ child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: <
127
+ Widget>[
128
Row(
129
mainAxisAlignment: MainAxisAlignment.start,
130
children: <Widget>[
131
Text(
132
_title,
133
style: TextStyle(
132
- fontSize: 18,
133
- fontWeight: FontWeight.w600,
134
- color: Theme.of(context).textTheme.headline.color
135
- ),
134
+ fontSize: 18,
135
+ fontWeight: FontWeight.w600,
136
+ color: Theme.of(context).textTheme.headline.color),
137
)
138
],
139
),
140
Padding(
140
- padding: EdgeInsets.only(top: 20),
141
- child: Stack(
142
- children: <Widget>[
143
- BaseTextFormField(
144
- controller: amountController,
145
- enabled: _isAmountEditable,
146
- textAlign: TextAlign.left,
147
- keyboardType: TextInputType.numberWithOptions(
148
- signed: false, decimal: true),
149
- // inputFormatters: [
150
- // LengthLimitingTextInputFormatter(15),
151
- // BlacklistingTextInputFormatter(
152
- // RegExp('[\\-|\\ |\\,]'))
153
- // ],
154
- hintText: '0.0000',
155
- borderColor: widget.borderColor,
156
- textStyle: TextStyle(
157
- fontSize: 16,
158
- fontWeight: FontWeight.w600,
159
- color: Colors.white
160
- ),
161
- placeholderTextStyle: TextStyle(
162
- fontSize: 16,
163
- fontWeight: FontWeight.w600,
164
- color: Theme.of(context).textTheme.subhead.decorationColor
165
- ),
166
- validator: _isAmountEditable
167
- ? widget.currencyValueValidator
168
- : null
169
- ),
170
- Positioned(
171
- top: 8,
172
- right: 0,
173
- child: Container(
174
- height: 32,
175
- padding: EdgeInsets.only(left: 10),
176
- color: widget.currencyButtonColor,
177
- child: InkWell(
178
- onTap: () => _presentPicker(context),
179
- child: Row(
180
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
181
- mainAxisSize: MainAxisSize.min,
182
- children: <Widget>[
183
- Text(
184
- _selectedCurrency.toString(),
185
- style: TextStyle(
186
- fontWeight: FontWeight.w600,
187
- fontSize: 16,
188
- color: Colors.white)),
189
- Padding(
190
- padding: EdgeInsets.only(left: 5),
191
- child: widget.imageArrow,
192
- )
193
- ]),
141
+ padding: EdgeInsets.only(top: 20),
142
+ child: Stack(
143
+ children: <Widget>[
144
+ BaseTextFormField(
145
+ focusNode: widget.amountFocusNode,
146
+ controller: amountController,
147
+ enabled: _isAmountEditable,
148
+ textAlign: TextAlign.left,
149
+ keyboardType: TextInputType.numberWithOptions(
150
+ signed: false, decimal: true),
151
+ // inputFormatters: [
152
+ // LengthLimitingTextInputFormatter(15),
153
+ // BlacklistingTextInputFormatter(
154
+ // RegExp('[\\-|\\ |\\,]'))
155
+ // ],
156
+ hintText: '0.0000',
157
+ borderColor: widget.borderColor,
158
+ textStyle: TextStyle(
159
+ fontSize: 16,
160
+ fontWeight: FontWeight.w600,
161
+ color: Colors.white),
162
+ placeholderTextStyle: TextStyle(
163
+ fontSize: 16,
164
+ fontWeight: FontWeight.w600,
165
+ color: Theme.of(context)
166
+ .textTheme
167
+ .subhead
168
+ .decorationColor),
169
+ validator: _isAmountEditable
170
+ ? widget.currencyValueValidator
171
+ : null),
172
+ Positioned(
173
+ top: 8,
174
+ right: 0,
175
+ child: Container(
176
+ height: 32,
177
+ padding: EdgeInsets.only(left: 10),
178
+ color: widget.currencyButtonColor,
179
+ child: InkWell(
180
+ onTap: () => _presentPicker(context),
181
+ child: Row(
182
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
183
+ mainAxisSize: MainAxisSize.min,
184
+ children: <Widget>[
185
+ Text(_selectedCurrency.toString(),
186
+ style: TextStyle(
187
+ fontWeight: FontWeight.w600,
188
+ fontSize: 16,
189
+ color: Colors.white)),
190
+ Padding(
191
+ padding: EdgeInsets.only(left: 5),
192
+ child: widget.imageArrow,
193
+ )
194
+ ]),
195
+ ),
196
),
195
- ),
196
- )
197
- ],
198
- )
199
- ),
197
+ )
198
+ ],
199
+ )),
200
Padding(
201
padding: EdgeInsets.only(top: 5),
202
- child: Row(
203
- mainAxisAlignment: MainAxisAlignment.start,
204
- children: <Widget>[
205
- _min != null
202
+ child: Row(mainAxisAlignment: MainAxisAlignment.start, children: <
203
+ Widget>[
204
+ _min != null
205
? Text(
207
- S.of(context).min_value(
208
- _min, _selectedCurrency.toString()),
209
- style: TextStyle(
210
- fontSize: 10,
211
- height: 1.2,
212
- color: Theme.of(context).textTheme.subhead.decorationColor),
213
- )
206
+ S.of(context).min_value(_min, _selectedCurrency.toString()),
207
+ style: TextStyle(
208
+ fontSize: 10,
209
+ height: 1.2,
210
+ color: Theme.of(context)
211
+ .textTheme
212
+ .subhead
213
+ .decorationColor),
214
+ )
215
: Offstage(),
215
- _min != null ? SizedBox(width: 10) : Offstage(),
216
- _max != null
216
+ _min != null ? SizedBox(width: 10) : Offstage(),
217
+ _max != null
218
? Text(
218
- S.of(context).max_value(
219
- _max, _selectedCurrency.toString()),
219
+ S.of(context).max_value(_max, _selectedCurrency.toString()),
220
style: TextStyle(
221
fontSize: 10,
222
height: 1.2,
223
- color: Theme.of(context).textTheme.subhead.decorationColor))
223
+ color: Theme.of(context)
224
+ .textTheme
225
+ .subhead
226
+ .decorationColor))
227
: Offstage(),
225
- ]),
228
+ ]),
229
),
230
_isAddressEditable
228
- ? Offstage()
229
- : Padding(
230
- padding: EdgeInsets.only(top: 20),
231
- child: Text(
232
- S.of(context).refund_address,
233
- style: TextStyle(
234
- fontSize: 14,
235
- fontWeight: FontWeight.w500,
236
- color: Theme.of(context).textTheme.subhead.decorationColor
237
- ),
238
- )
239
- ),
231
+ ? Offstage()
232
+ : Padding(
233
+ padding: EdgeInsets.only(top: 20),
234
+ child: Text(
235
+ S.of(context).refund_address,
236
+ style: TextStyle(
237
+ fontSize: 14,
238
+ fontWeight: FontWeight.w500,
239
+ color:
240
+ Theme.of(context).textTheme.subhead.decorationColor),
241
+ )),
242
_isAddressEditable
241
- ? Padding(
242
- padding: EdgeInsets.only(top: 20),
243
- child: AddressTextField(
244
- controller: addressController,
245
- options: [
246
- AddressTextFieldOption.paste,
247
- AddressTextFieldOption.qrCode,
248
- AddressTextFieldOption.addressBook,
249
- ],
250
- isBorderExist: false,
251
- textStyle: TextStyle(
252
- fontSize: 16,
253
- fontWeight: FontWeight.w600,
254
- color: Colors.white),
255
- hintStyle: TextStyle(
256
- fontSize: 16,
257
- fontWeight: FontWeight.w600,
258
- color: Theme.of(context).textTheme.subhead.decorationColor),
259
- buttonColor: widget.addressButtonsColor,
260
- validator: widget.addressTextFieldValidator,
261
- ),
262
- )
263
- : Padding(
264
- padding: EdgeInsets.only(top: 10),
265
- child: Builder(
266
- builder: (context) => GestureDetector(
267
- onTap: () {
268
- Clipboard.setData(ClipboardData(
269
- text: addressController.text));
270
- Scaffold.of(context).showSnackBar(SnackBar(
271
- content: Text(
272
- S.of(context).copied_to_clipboard,
273
- style: TextStyle(color: Colors.white),
274
- ),
275
- backgroundColor: Colors.green,
276
- duration: Duration(milliseconds: 500),
277
- ));
278
- },
279
- child: Row(
280
- mainAxisSize: MainAxisSize.max,
281
- children: <Widget>[
282
- Expanded(
283
- child: Text(
284
- addressController.text,
285
- maxLines: 1,
286
- overflow: TextOverflow.ellipsis,
287
- style: TextStyle(
288
- fontSize: 16,
289
- fontWeight: FontWeight.w600,
290
- color: Colors.white),
291
- ),
292
- ),
293
- Padding(
294
- padding: EdgeInsets.only(left: 16),
295
- child: copyImage,
296
- )
243
+ ? Padding(
244
+ padding: EdgeInsets.only(top: 20),
245
+ child: AddressTextField(
246
+ controller: addressController,
247
+ options: [
248
+ AddressTextFieldOption.paste,
249
+ AddressTextFieldOption.qrCode,
250
+ AddressTextFieldOption.addressBook,
251
],
252
+ isBorderExist: false,
253
+ textStyle: TextStyle(
254
+ fontSize: 16,
255
+ fontWeight: FontWeight.w600,
256
+ color: Colors.white),
257
+ hintStyle: TextStyle(
258
+ fontSize: 16,
259
+ fontWeight: FontWeight.w600,
260
+ color:
261
+ Theme.of(context).textTheme.subhead.decorationColor),
262
+ buttonColor: widget.addressButtonsColor,
263
+ validator: widget.addressTextFieldValidator,
264
),
265
)
300
- ),
301
- ),
266
+ : Padding(
267
+ padding: EdgeInsets.only(top: 10),
268
+ child: Builder(
269
+ builder: (context) => GestureDetector(
270
+ onTap: () {
271
+ Clipboard.setData(
272
+ ClipboardData(text: addressController.text));
273
+ Scaffold.of(context).showSnackBar(SnackBar(
274
+ content: Text(
275
+ S.of(context).copied_to_clipboard,
276
+ style: TextStyle(color: Colors.white),
277
+ ),
278
+ backgroundColor: Colors.green,
279
+ duration: Duration(milliseconds: 500),
280
+ ));
281
+ },
282
+ child: Row(
283
+ mainAxisSize: MainAxisSize.max,
284
+ children: <Widget>[
285
+ Expanded(
286
+ child: Text(
287
+ addressController.text,
288
+ maxLines: 1,
289
+ overflow: TextOverflow.ellipsis,
290
+ style: TextStyle(
291
+ fontSize: 16,
292
+ fontWeight: FontWeight.w600,
293
+ color: Colors.white),
294
+ ),
295
+ ),
296
+ Padding(
297
+ padding: EdgeInsets.only(left: 16),
298
+ child: copyImage,
299
+ )
300
+ ],
301
+ ),
302
+ )),
303
+ ),
304
]),
305
);
306
}
@@ -310,9 +312,9 @@ class ExchangeCardState extends State<ExchangeCard> {
312
items: widget.currencies,
313
title: S.of(context).change_currency,
314
onItemSelected: (CryptoCurrency item) =>
313
- widget.onCurrencySelected != null
314
- ? widget.onCurrencySelected(item)
315
- : null),
315
+ widget.onCurrencySelected != null
316
+ ? widget.onCurrencySelected(item)
317
+ : null),
318
context: context);
319
}
320
}
lib/src/screens/root/root.dart
+8
-2
@@ -6,11 +6,17 @@ import 'package:cake_wallet/store/authentication_store.dart';
6
import 'package:cake_wallet/entities/qr_scanner.dart';
7
8
class Root extends StatefulWidget {
9
- Root({Key key, this.authenticationStore, this.appStore, this.child})
9
+ Root(
10
+ {Key key,
11
+ this.authenticationStore,
12
+ this.appStore,
13
+ this.child,
14
+ this.navigatorKey})
15
: super(key: key);
16
17
final AuthenticationStore authenticationStore;
18
final AppStore appStore;
19
+ final GlobalKey<NavigatorState> navigatorKey;
20
final Widget child;
21
22
@override
@@ -53,7 +59,7 @@ class RootState extends State<Root> with WidgetsBindingObserver {
59
if (_isInactive && !_postFrameCallback) {
60
_postFrameCallback = true;
61
WidgetsBinding.instance.addPostFrameCallback((_) {
56
- Navigator.of(context).pushNamed(Routes.unlock,
62
+ widget.navigatorKey.currentState.pushNamed(Routes.unlock,
63
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
64
if (!isAuthenticatedSuccessfully) {
65
return;
lib/src/screens/send/send_page.dart
+405
-374
@@ -1,31 +1,26 @@
1
import 'dart:ui';
2
-
3
-// import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
2
+import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
3
+import 'package:flutter/cupertino.dart';
4
+import 'package:flutter/material.dart';
5
+import 'package:flutter/services.dart';
6
+import 'package:flutter_mobx/flutter_mobx.dart';
7
+import 'package:mobx/mobx.dart';
8
+import 'package:keyboard_actions/keyboard_actions.dart';
9
+import 'package:cake_wallet/routes.dart';
10
+import 'package:cake_wallet/view_model/send/send_view_model.dart';
11
import 'package:cake_wallet/core/execution_state.dart';
12
import 'package:cake_wallet/src/screens/base_page.dart';
6
-import 'package:cake_wallet/src/widgets/picker.dart';
13
import 'package:cake_wallet/src/widgets/primary_button.dart';
14
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
15
import 'package:cake_wallet/src/widgets/trail_button.dart';
16
import 'package:cake_wallet/utils/show_pop_up.dart';
17
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
12
-import 'package:flutter/cupertino.dart';
13
-import 'package:flutter/material.dart';
14
-import 'package:cake_wallet/view_model/send/send_view_model.dart';
15
-import 'package:flutter/services.dart';
16
-import 'package:flutter_mobx/flutter_mobx.dart';
17
-import 'package:mobx/mobx.dart';
18
import 'package:cake_wallet/src/widgets/address_text_field.dart';
19
import 'package:cake_wallet/generated/i18n.dart';
20
import 'package:dotted_border/dotted_border.dart';
21
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
22
-import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
22
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
24
-
25
-// import 'package:cake_wallet/src/screens/send/widgets/sending_alert.dart';
26
-import 'package:cake_wallet/src/widgets/template_tile.dart';
23
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
28
-import 'package:cake_wallet/routes.dart';
24
25
class SendPage extends BasePage {
26
SendPage({@required this.sendViewModel});
@@ -34,8 +29,9 @@ class SendPage extends BasePage {
29
final _addressController = TextEditingController();
30
final _cryptoAmountController = TextEditingController();
31
final _fiatAmountController = TextEditingController();
37
- final _focusNode = FocusNode();
32
final _formKey = GlobalKey<FormState>();
33
+ final _cryptoAmountFocus = FocusNode();
34
+ final _fiatAmountFocus = FocusNode();
35
36
bool _effectsInstalled = false;
37
@@ -62,180 +58,75 @@ class SendPage extends BasePage {
58
Widget body(BuildContext context) {
59
_setEffects(context);
60
65
- return Container(
66
- color: Theme.of(context).backgroundColor,
67
- child: ScrollableWithBottomSection(
68
- contentPadding: EdgeInsets.only(bottom: 24),
69
- content: Column(
70
- children: <Widget>[
71
- Container(
72
- decoration: BoxDecoration(
73
- borderRadius: BorderRadius.only(
74
- bottomLeft: Radius.circular(24),
75
- bottomRight: Radius.circular(24)),
76
- gradient: LinearGradient(colors: [
77
- Theme.of(context).primaryTextTheme.subhead.color,
78
- Theme.of(context).primaryTextTheme.subhead.decorationColor,
79
- ], begin: Alignment.topLeft, end: Alignment.bottomRight),
80
- ),
81
- child: Form(
82
- key: _formKey,
83
- child: Column(children: <Widget>[
84
- Padding(
85
- padding: EdgeInsets.fromLTRB(24, 90, 24, 32),
86
- child: Column(
87
- children: <Widget>[
88
- AddressTextField(
89
- controller: _addressController,
90
- focusNode: _focusNode,
91
- onURIScanned: (uri) {
92
- var address = '';
93
- var amount = '';
94
-
95
- if (uri != null) {
96
- address = uri.path;
97
- amount = uri.queryParameters['tx_amount'];
98
- } else {
99
- address = uri.toString();
100
- }
101
-
102
- _addressController.text = address;
103
- _cryptoAmountController.text = amount;
104
- },
105
- options: [
106
- AddressTextFieldOption.paste,
107
- AddressTextFieldOption.qrCode,
108
- AddressTextFieldOption.addressBook
109
- ],
110
- buttonColor: Theme.of(context)
111
- .primaryTextTheme
112
- .display1
113
- .color,
114
- borderColor: Theme.of(context)
115
- .primaryTextTheme
116
- .headline
117
- .color,
118
- textStyle: TextStyle(
119
- fontSize: 14,
120
- fontWeight: FontWeight.w500,
121
- color: Colors.white),
122
- hintStyle: TextStyle(
123
- fontSize: 14,
124
- fontWeight: FontWeight.w500,
125
- color: Theme.of(context)
61
+ return KeyboardActions(
62
+ config: KeyboardActionsConfig(
63
+ keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
64
+ keyboardBarColor: isDarkTheme
65
+ ? Color.fromRGBO(48, 51, 60, 1.0)
66
+ : Color.fromRGBO(98, 98, 98, 1.0),
67
+ nextFocus: false,
68
+ actions: [
69
+ KeyboardActionsItem(
70
+ focusNode: _cryptoAmountFocus,
71
+ toolbarButtons: [(_) => KeyboardDoneButton()],
72
+ ),
73
+ KeyboardActionsItem(
74
+ focusNode: _fiatAmountFocus,
75
+ toolbarButtons: [(_) => KeyboardDoneButton()],
76
+ )
77
+ ]),
78
+ child: Container(
79
+ height: 0,
80
+ color: Theme.of(context).backgroundColor,
81
+ child: ScrollableWithBottomSection(
82
+ contentPadding: EdgeInsets.only(bottom: 24),
83
+ content: Column(
84
+ children: <Widget>[
85
+ Container(
86
+ decoration: BoxDecoration(
87
+ borderRadius: BorderRadius.only(
88
+ bottomLeft: Radius.circular(24),
89
+ bottomRight: Radius.circular(24)),
90
+ gradient: LinearGradient(colors: [
91
+ Theme.of(context).primaryTextTheme.subhead.color,
92
+ Theme.of(context)
93
+ .primaryTextTheme
94
+ .subhead
95
+ .decorationColor,
96
+ ], begin: Alignment.topLeft, end: Alignment.bottomRight),
97
+ ),
98
+ child: Form(
99
+ key: _formKey,
100
+ child: Column(children: <Widget>[
101
+ Padding(
102
+ padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
103
+ child: Column(
104
+ children: <Widget>[
105
+ AddressTextField(
106
+ controller: _addressController,
107
+ onURIScanned: (uri) {
108
+ var address = '';
109
+ var amount = '';
110
+
111
+ if (uri != null) {
112
+ address = uri.path;
113
+ amount = uri.queryParameters['tx_amount'];
114
+ } else {
115
+ address = uri.toString();
116
+ }
117
+
118
+ _addressController.text = address;
119
+ _cryptoAmountController.text = amount;
120
+ },
121
+ options: [
122
+ AddressTextFieldOption.paste,
123
+ AddressTextFieldOption.qrCode,
124
+ AddressTextFieldOption.addressBook
125
+ ],
126
+ buttonColor: Theme.of(context)
127
.primaryTextTheme
127
- .headline
128
- .decorationColor),
129
- validator: sendViewModel.addressValidator,
130
- ),
131
- Padding(
132
- padding: const EdgeInsets.only(top: 20),
133
- child: BaseTextFormField(
134
- controller: _cryptoAmountController,
135
- keyboardType: TextInputType.numberWithOptions(
136
- signed: false, decimal: true),
137
- prefixIcon: Padding(
138
- padding: EdgeInsets.only(top: 9),
139
- child:
140
- Text(sendViewModel.currency.title + ':',
141
- style: TextStyle(
142
- fontSize: 16,
143
- fontWeight: FontWeight.w600,
144
- color: Colors.white,
145
- )),
146
- ),
147
- suffixIcon: Container(
148
- height: 32,
149
- width: 32,
150
- margin: EdgeInsets.only(
151
- left: 14, top: 4, bottom: 10),
152
- decoration: BoxDecoration(
153
- color: Theme.of(context)
154
- .primaryTextTheme
155
- .display1
156
- .color,
157
- borderRadius: BorderRadius.all(
158
- Radius.circular(6))),
159
- child: InkWell(
160
- onTap: () => sendViewModel.setSendAll(),
161
- child: Center(
162
- child: Text(S.of(context).all,
163
- textAlign: TextAlign.center,
164
- style: TextStyle(
165
- fontSize: 12,
166
- fontWeight: FontWeight.bold,
167
- color: Theme.of(context)
168
- .primaryTextTheme
169
- .display1
170
- .decorationColor)),
171
- ),
172
- ),
173
- ),
174
- hintText: '0.0000',
175
- borderColor: Theme.of(context)
176
- .primaryTextTheme
177
- .headline
178
- .color,
179
- textStyle: TextStyle(
180
- fontSize: 14,
181
- fontWeight: FontWeight.w500,
182
- color: Colors.white),
183
- placeholderTextStyle: TextStyle(
184
- color: Theme.of(context)
185
- .primaryTextTheme
186
- .headline
187
- .decorationColor,
188
- fontWeight: FontWeight.w500,
189
- fontSize: 14),
190
- validator: sendViewModel.amountValidator)),
191
- Observer(
192
- builder: (_) => Padding(
193
- padding: EdgeInsets.only(top: 10),
194
- child: Row(
195
- mainAxisSize: MainAxisSize.max,
196
- mainAxisAlignment:
197
- MainAxisAlignment.spaceBetween,
198
- children: <Widget>[
199
- Expanded(
200
- child: Text(
201
- S.of(context).available_balance + ':',
202
- style: TextStyle(
203
- fontSize: 12,
204
- fontWeight: FontWeight.w600,
205
- color: Theme.of(context)
206
- .primaryTextTheme
207
- .headline
208
- .decorationColor),
209
- )),
210
- Text(
211
- sendViewModel.balance,
212
- style: TextStyle(
213
- fontSize: 12,
214
- fontWeight: FontWeight.w600,
215
- color: Theme.of(context)
216
- .primaryTextTheme
217
- .headline
218
- .decorationColor),
219
- )
220
- ],
221
- ),
222
- )),
223
- Padding(
224
- padding: const EdgeInsets.only(top: 20),
225
- child: BaseTextFormField(
226
- controller: _fiatAmountController,
227
- keyboardType: TextInputType.numberWithOptions(
228
- signed: false, decimal: true),
229
- prefixIcon: Padding(
230
- padding: EdgeInsets.only(top: 9),
231
- child: Text(sendViewModel.fiat.title + ':',
232
- style: TextStyle(
233
- fontSize: 16,
234
- fontWeight: FontWeight.w600,
235
- color: Colors.white,
236
- )),
237
- ),
238
- hintText: '0.00',
128
+ .display1
129
+ .color,
130
borderColor: Theme.of(context)
131
.primaryTextTheme
132
.headline
@@ -244,202 +135,342 @@ class SendPage extends BasePage {
135
fontSize: 14,
136
fontWeight: FontWeight.w500,
137
color: Colors.white),
247
- placeholderTextStyle: TextStyle(
138
+ hintStyle: TextStyle(
139
+ fontSize: 14,
140
+ fontWeight: FontWeight.w500,
141
color: Theme.of(context)
142
.primaryTextTheme
143
.headline
251
- .decorationColor,
252
- fontWeight: FontWeight.w500,
253
- fontSize: 14),
254
- )),
255
- Observer(
256
- builder: (_) => GestureDetector(
257
- onTap: () =>
258
- _setTransactionPriority(context),
259
- child: Container(
260
- padding: EdgeInsets.only(top: 24),
261
- child: Row(
262
- mainAxisAlignment:
263
- MainAxisAlignment.spaceBetween,
264
- children: <Widget>[
265
- Text(S.of(context).send_estimated_fee,
144
+ .decorationColor),
145
+ validator: sendViewModel.addressValidator,
146
+ ),
147
+ Padding(
148
+ padding: const EdgeInsets.only(top: 20),
149
+ child: BaseTextFormField(
150
+ focusNode: _cryptoAmountFocus,
151
+ controller: _cryptoAmountController,
152
+ keyboardType:
153
+ TextInputType.numberWithOptions(
154
+ signed: false, decimal: true),
155
+ prefixIcon: Padding(
156
+ padding: EdgeInsets.only(top: 9),
157
+ child: Text(
158
+ sendViewModel.currency.title + ':',
159
+ style: TextStyle(
160
+ fontSize: 16,
161
+ fontWeight: FontWeight.w600,
162
+ color: Colors.white,
163
+ )),
164
+ ),
165
+ suffixIcon: Container(
166
+ height: 32,
167
+ width: 32,
168
+ margin: EdgeInsets.only(
169
+ left: 14, top: 4, bottom: 10),
170
+ decoration: BoxDecoration(
171
+ color: Theme.of(context)
172
+ .primaryTextTheme
173
+ .display1
174
+ .color,
175
+ borderRadius: BorderRadius.all(
176
+ Radius.circular(6))),
177
+ child: InkWell(
178
+ onTap: () =>
179
+ sendViewModel.setSendAll(),
180
+ child: Center(
181
+ child: Text(S.of(context).all,
182
+ textAlign: TextAlign.center,
183
+ style: TextStyle(
184
+ fontSize: 12,
185
+ fontWeight: FontWeight.bold,
186
+ color: Theme.of(context)
187
+ .primaryTextTheme
188
+ .display1
189
+ .decorationColor)),
190
+ ),
191
+ ),
192
+ ),
193
+ hintText: '0.0000',
194
+ borderColor: Theme.of(context)
195
+ .primaryTextTheme
196
+ .headline
197
+ .color,
198
+ textStyle: TextStyle(
199
+ fontSize: 14,
200
+ fontWeight: FontWeight.w500,
201
+ color: Colors.white),
202
+ placeholderTextStyle: TextStyle(
203
+ color: Theme.of(context)
204
+ .primaryTextTheme
205
+ .headline
206
+ .decorationColor,
207
+ fontWeight: FontWeight.w500,
208
+ fontSize: 14),
209
+ validator:
210
+ sendViewModel.amountValidator)),
211
+ Observer(
212
+ builder: (_) => Padding(
213
+ padding: EdgeInsets.only(top: 10),
214
+ child: Row(
215
+ mainAxisSize: MainAxisSize.max,
216
+ mainAxisAlignment:
217
+ MainAxisAlignment.spaceBetween,
218
+ children: <Widget>[
219
+ Expanded(
220
+ child: Text(
221
+ S.of(context).available_balance +
222
+ ':',
223
+ style: TextStyle(
224
+ fontSize: 12,
225
+ fontWeight: FontWeight.w600,
226
+ color: Theme.of(context)
227
+ .primaryTextTheme
228
+ .headline
229
+ .decorationColor),
230
+ )),
231
+ Text(
232
+ sendViewModel.balance,
233
style: TextStyle(
234
fontSize: 12,
268
- fontWeight: FontWeight.w500,
269
- //color: Theme.of(context).primaryTextTheme.display2.color,
270
- color: Colors.white)),
271
- Container(
272
- child: Row(
273
- children: <Widget>[
274
- Text(
275
- sendViewModel.estimatedFee
276
- .toString() +
277
- ' ' +
235
+ fontWeight: FontWeight.w600,
236
+ color: Theme.of(context)
237
+ .primaryTextTheme
238
+ .headline
239
+ .decorationColor),
240
+ )
241
+ ],
242
+ ),
243
+ )),
244
+ Padding(
245
+ padding: const EdgeInsets.only(top: 20),
246
+ child: BaseTextFormField(
247
+ focusNode: _fiatAmountFocus,
248
+ controller: _fiatAmountController,
249
+ keyboardType:
250
+ TextInputType.numberWithOptions(
251
+ signed: false, decimal: true),
252
+ prefixIcon: Padding(
253
+ padding: EdgeInsets.only(top: 9),
254
+ child:
255
+ Text(sendViewModel.fiat.title + ':',
256
+ style: TextStyle(
257
+ fontSize: 16,
258
+ fontWeight: FontWeight.w600,
259
+ color: Colors.white,
260
+ )),
261
+ ),
262
+ hintText: '0.00',
263
+ borderColor: Theme.of(context)
264
+ .primaryTextTheme
265
+ .headline
266
+ .color,
267
+ textStyle: TextStyle(
268
+ fontSize: 14,
269
+ fontWeight: FontWeight.w500,
270
+ color: Colors.white),
271
+ placeholderTextStyle: TextStyle(
272
+ color: Theme.of(context)
273
+ .primaryTextTheme
274
+ .headline
275
+ .decorationColor,
276
+ fontWeight: FontWeight.w500,
277
+ fontSize: 14),
278
+ )),
279
+ Observer(
280
+ builder: (_) => GestureDetector(
281
+ onTap: () =>
282
+ _setTransactionPriority(context),
283
+ child: Container(
284
+ padding: EdgeInsets.only(top: 24),
285
+ child: Row(
286
+ mainAxisAlignment:
287
+ MainAxisAlignment.spaceBetween,
288
+ children: <Widget>[
289
+ Text(
290
+ S
291
+ .of(context)
292
+ .send_estimated_fee,
293
+ style: TextStyle(
294
+ fontSize: 12,
295
+ fontWeight:
296
+ FontWeight.w500,
297
+ //color: Theme.of(context).primaryTextTheme.display2.color,
298
+ color: Colors.white)),
299
+ Container(
300
+ child: Row(
301
+ children: <Widget>[
302
+ Text(
303
sendViewModel
279
- .currency.title,
280
- style: TextStyle(
281
- fontSize: 12,
282
- fontWeight:
283
- FontWeight.w600,
284
- //color: Theme.of(context).primaryTextTheme.display2.color,
285
- color: Colors.white)),
286
- Padding(
287
- padding:
288
- EdgeInsets.only(left: 5),
289
- child: Icon(
290
- Icons.arrow_forward_ios,
291
- size: 12,
292
- color: Colors.white,
293
- ),
294
- )
295
- ],
296
- ),
297
- )
298
- ],
299
- ),
304
+ .estimatedFee
305
+ .toString() +
306
+ ' ' +
307
+ sendViewModel
308
+ .currency.title,
309
+ style: TextStyle(
310
+ fontSize: 12,
311
+ fontWeight:
312
+ FontWeight.w600,
313
+ //color: Theme.of(context).primaryTextTheme.display2.color,
314
+ color:
315
+ Colors.white)),
316
+ Padding(
317
+ padding: EdgeInsets.only(
318
+ left: 5),
319
+ child: Icon(
320
+ Icons.arrow_forward_ios,
321
+ size: 12,
322
+ color: Colors.white,
323
+ ),
324
+ )
325
+ ],
326
+ ),
327
+ )
328
+ ],
329
+ ),
330
+ ),
331
+ ))
332
+ ],
333
+ ),
334
+ )
335
+ ]),
336
+ ),
337
+ ),
338
+ Padding(
339
+ padding: EdgeInsets.only(top: 30, left: 24, bottom: 24),
340
+ child: Row(
341
+ mainAxisAlignment: MainAxisAlignment.start,
342
+ children: <Widget>[
343
+ Text(
344
+ S.of(context).send_templates,
345
+ style: TextStyle(
346
+ fontSize: 18,
347
+ fontWeight: FontWeight.w600,
348
+ color: Theme.of(context)
349
+ .primaryTextTheme
350
+ .display4
351
+ .color),
352
+ )
353
+ ],
354
+ ),
355
+ ),
356
+ Container(
357
+ height: 40,
358
+ width: double.infinity,
359
+ padding: EdgeInsets.only(left: 24),
360
+ child: SingleChildScrollView(
361
+ scrollDirection: Axis.horizontal,
362
+ child: Row(
363
+ children: <Widget>[
364
+ GestureDetector(
365
+ onTap: () => Navigator.of(context)
366
+ .pushNamed(Routes.sendTemplate),
367
+ child: Container(
368
+ padding: EdgeInsets.only(left: 1, right: 10),
369
+ child: DottedBorder(
370
+ borderType: BorderType.RRect,
371
+ dashPattern: [6, 4],
372
+ color: Theme.of(context)
373
+ .primaryTextTheme
374
+ .display2
375
+ .decorationColor,
376
+ strokeWidth: 2,
377
+ radius: Radius.circular(20),
378
+ child: Container(
379
+ height: 34,
380
+ width: 75,
381
+ padding:
382
+ EdgeInsets.only(left: 10, right: 10),
383
+ alignment: Alignment.center,
384
+ decoration: BoxDecoration(
385
+ borderRadius:
386
+ BorderRadius.all(Radius.circular(20)),
387
+ color: Colors.transparent,
388
+ ),
389
+ child: Text(
390
+ S.of(context).send_new,
391
+ style: TextStyle(
392
+ fontSize: 14,
393
+ fontWeight: FontWeight.w600,
394
+ color: Theme.of(context)
395
+ .primaryTextTheme
396
+ .display3
397
+ .color),
398
),
301
- ))
399
+ )),
400
+ ),
401
+ ),
402
+ // Observer(
403
+ // builder: (_) {
404
+ // final templates = sendViewModel.templates;
405
+ // final itemCount = templates.length;
406
+
407
+ // return ListView.builder(
408
+ // scrollDirection: Axis.horizontal,
409
+ // shrinkWrap: true,
410
+ // physics: NeverScrollableScrollPhysics(),
411
+ // itemCount: itemCount,
412
+ // itemBuilder: (context, index) {
413
+ // final template = templates[index];
414
+
415
+ // return TemplateTile(
416
+ // key: UniqueKey(),
417
+ // to: template.name,
418
+ // amount: template.amount,
419
+ // from: template.cryptoCurrency,
420
+ // onTap: () {
421
+ // _addressController.text = template.address;
422
+ // _cryptoAmountController.text = template.amount;
423
+ // getOpenaliasRecord(context);
424
+ // },
425
+ // onRemove: () {
426
+ // showPopUp<void>(
427
+ // context: context,
428
+ // builder: (dialogContext) {
429
+ // return AlertWithTwoActions(
430
+ // alertTitle: S.of(context).template,
431
+ // alertContent: S.of(context).confirm_delete_template,
432
+ // leftButtonText: S.of(context).delete,
433
+ // rightButtonText: S.of(context).cancel,
434
+ // actionLeftButton: () {
435
+ // Navigator.of(dialogContext).pop();
436
+ // sendViewModel.sendTemplateStore.remove(template: template);
437
+ // sendViewModel.sendTemplateStore.update();
438
+ // },
439
+ // actionRightButton: () => Navigator.of(dialogContext).pop()
440
+ // );
441
+ // }
442
+ // );
443
+ // },
444
+ // );
445
+ // }
446
+ // );
447
+ // }
448
+ // )
449
],
450
),
304
- )
305
- ]),
306
- ),
307
- ),
308
- Padding(
309
- padding: EdgeInsets.only(top: 30, left: 24, bottom: 24),
310
- child: Row(
311
- mainAxisAlignment: MainAxisAlignment.start,
312
- children: <Widget>[
313
- Text(
314
- S.of(context).send_templates,
315
- style: TextStyle(
316
- fontSize: 18,
317
- fontWeight: FontWeight.w600,
318
- color: Theme.of(context)
319
- .primaryTextTheme
320
- .display4
321
- .color),
322
- )
323
- ],
324
- ),
451
+ ),
452
+ )
453
+ ],
454
),
326
- Container(
327
- height: 40,
328
- width: double.infinity,
329
- padding: EdgeInsets.only(left: 24),
330
- child: SingleChildScrollView(
331
- scrollDirection: Axis.horizontal,
332
- child: Row(
333
- children: <Widget>[
334
- GestureDetector(
335
- onTap: () => Navigator.of(context)
336
- .pushNamed(Routes.sendTemplate),
337
- child: Container(
338
- padding: EdgeInsets.only(left: 1, right: 10),
339
- child: DottedBorder(
340
- borderType: BorderType.RRect,
341
- dashPattern: [6, 4],
342
- color: Theme.of(context)
343
- .primaryTextTheme
344
- .display2
345
- .decorationColor,
346
- strokeWidth: 2,
347
- radius: Radius.circular(20),
348
- child: Container(
349
- height: 34,
350
- width: 75,
351
- padding: EdgeInsets.only(left: 10, right: 10),
352
- alignment: Alignment.center,
353
- decoration: BoxDecoration(
354
- borderRadius:
355
- BorderRadius.all(Radius.circular(20)),
356
- color: Colors.transparent,
357
- ),
358
- child: Text(
359
- S.of(context).send_new,
360
- style: TextStyle(
361
- fontSize: 14,
362
- fontWeight: FontWeight.w600,
363
- color: Theme.of(context)
364
- .primaryTextTheme
365
- .display3
366
- .color),
367
- ),
368
- )),
369
- ),
370
- ),
371
- // Observer(
372
- // builder: (_) {
373
- // final templates = sendViewModel.templates;
374
- // final itemCount = templates.length;
375
-
376
- // return ListView.builder(
377
- // scrollDirection: Axis.horizontal,
378
- // shrinkWrap: true,
379
- // physics: NeverScrollableScrollPhysics(),
380
- // itemCount: itemCount,
381
- // itemBuilder: (context, index) {
382
- // final template = templates[index];
383
-
384
- // return TemplateTile(
385
- // key: UniqueKey(),
386
- // to: template.name,
387
- // amount: template.amount,
388
- // from: template.cryptoCurrency,
389
- // onTap: () {
390
- // _addressController.text = template.address;
391
- // _cryptoAmountController.text = template.amount;
392
- // getOpenaliasRecord(context);
393
- // },
394
- // onRemove: () {
395
- // showPopUp<void>(
396
- // context: context,
397
- // builder: (dialogContext) {
398
- // return AlertWithTwoActions(
399
- // alertTitle: S.of(context).template,
400
- // alertContent: S.of(context).confirm_delete_template,
401
- // leftButtonText: S.of(context).delete,
402
- // rightButtonText: S.of(context).cancel,
403
- // actionLeftButton: () {
404
- // Navigator.of(dialogContext).pop();
405
- // sendViewModel.sendTemplateStore.remove(template: template);
406
- // sendViewModel.sendTemplateStore.update();
407
- // },
408
- // actionRightButton: () => Navigator.of(dialogContext).pop()
409
- // );
410
- // }
411
- // );
412
- // },
413
- // );
414
- // }
415
- // );
416
- // }
417
- // )
418
- ],
419
- ),
420
- ),
421
- )
422
- ],
423
- ),
424
- bottomSectionPadding:
425
- EdgeInsets.only(left: 24, right: 24, bottom: 24),
426
- bottomSection: Observer(builder: (_) {
427
- return LoadingPrimaryButton(
428
- onPressed: () async {
429
- if (_formKey.currentState.validate()) {
430
- await sendViewModel.createTransaction();
431
- }
432
- },
433
- text: S.of(context).send,
434
- color: Theme.of(context).accentTextTheme.body2.color,
435
- textColor: Colors.white,
436
- isLoading: sendViewModel.state is IsExecutingState ||
437
- sendViewModel.state is TransactionCommitting,
438
- isDisabled:
439
- false // FIXME !(syncStore.status is SyncedSyncStatus),
440
- );
441
- })),
442
- );
455
+ bottomSectionPadding:
456
+ EdgeInsets.only(left: 24, right: 24, bottom: 24),
457
+ bottomSection: Observer(builder: (_) {
458
+ return LoadingPrimaryButton(
459
+ onPressed: () async {
460
+ if (_formKey.currentState.validate()) {
461
+ await sendViewModel.createTransaction();
462
+ }
463
+ },
464
+ text: S.of(context).send,
465
+ color: Theme.of(context).accentTextTheme.body2.color,
466
+ textColor: Colors.white,
467
+ isLoading: sendViewModel.state is IsExecutingState ||
468
+ sendViewModel.state is TransactionCommitting,
469
+ isDisabled:
470
+ false // FIXME !(syncStore.status is SyncedSyncStatus),
471
+ );
472
+ })),
473
+ ));
474
}
475
476
void _setEffects(BuildContext context) {
lib/src/widgets/base_text_form_field.dart
+4
-1
@@ -22,7 +22,8 @@ class BaseTextFormField extends StatelessWidget {
22
this.validator,
23
this.textStyle,
24
this.placeholderTextStyle,
25
- this.maxLength});
25
+ this.maxLength,
26
+ this.focusNode});
27
28
final TextEditingController controller;
29
final TextInputType keyboardType;
@@ -44,10 +45,12 @@ class BaseTextFormField extends StatelessWidget {
45
final TextStyle placeholderTextStyle;
46
final TextStyle textStyle;
47
final int maxLength;
48
+ final FocusNode focusNode;
49
50
@override
51
Widget build(BuildContext context) {
52
return TextFormField(
53
+ focusNode: focusNode,
54
controller: controller,
55
keyboardType: keyboardType,
56
textInputAction: textInputAction,
lib/src/widgets/keyboard_done_button.dart
new
+19
@@ -0,0 +1,19 @@
1
+import 'package:flutter/cupertino.dart';
2
+import 'package:flutter/material.dart';
3
+
4
+class KeyboardDoneButton extends StatelessWidget {
5
+ @override
6
+ Widget build(BuildContext context) {
7
+ // FIXME: Add translation
8
+ return CupertinoButton(
9
+ padding: EdgeInsets.only(right: 24.0, top: 8.0, bottom: 8.0),
10
+ onPressed: () {
11
+ FocusScope.of(context).requestFocus(FocusNode());
12
+ },
13
+ child: Text(
14
+ 'Done',
15
+ style: TextStyle(color: Colors.blueAccent,fontWeight: FontWeight.bold)
16
+ ),
17
+ );
18
+ }
19
+}
\ No newline at end of file
pubspec.lock
+7
@@ -534,6 +534,13 @@ packages:
534
url: "https://pub.dartlang.org"
535
source: hosted
536
version: "3.1.0"
537
+ keyboard_actions:
538
+ dependency: "direct main"
539
+ description:
540
+ name: keyboard_actions
541
+ url: "https://pub.dartlang.org"
542
+ source: hosted
543
+ version: "3.3.0+1"
544
local_auth:
545
dependency: "direct main"
546
description:
pubspec.yaml
+1
@@ -64,6 +64,7 @@ dependencies:
64
bitcoin_flutter: ^2.0.0
65
get_it: ^4.0.2
66
connectivity: ^0.4.9+2
67
+ keyboard_actions: ^3.3.0
68
69
dev_dependencies:
70
flutter_test: