dev
dart 321 lines 14.8 KB
Raw
1 import 'package:cake_wallet/entities/qr_view_data.dart';
2 import 'package:cake_wallet/src/widgets/primary_button.dart';
3 import 'package:cake_wallet/routes.dart';
4 import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
5 import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dart';
6 import 'package:cake_wallet/generated/i18n.dart';
7 import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
8 import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
9 import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
10 import 'package:cake_wallet/utils/address_formatter.dart';
11 import 'package:cake_wallet/utils/brightness_util.dart';
12 import 'package:cake_wallet/utils/responsive_layout_util.dart';
13 import 'package:cake_wallet/utils/show_bar.dart';
14 import 'package:cake_wallet/utils/show_pop_up.dart';
15 import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
16 import 'package:cw_core/crypto_currency.dart';
17 import 'package:cw_core/payment_uris.dart';
18 import 'package:flutter/material.dart';
19 import 'package:flutter/services.dart';
20 import 'package:flutter_mobx/flutter_mobx.dart';
21 import 'package:url_launcher/url_launcher.dart';
22
23 class QRWidget extends StatelessWidget {
24 QRWidget({
25 required this.addressListViewModel,
26 this.qrVersion,
27 this.heroTag,
28 required this.amountController,
29 required this.formKey,
30 this.amountTextFieldFocusNode,
31 });
32
33 final WalletAddressListViewModel addressListViewModel;
34 final TextEditingController amountController;
35 final FocusNode? amountTextFieldFocusNode;
36 final GlobalKey<FormState> formKey;
37 final int? qrVersion;
38 final String? heroTag;
39
40 PaymentURI get addressUri {
41 return addressListViewModel.uri;
42 }
43
44 @override
45 Widget build(BuildContext context) {
46 final copyImage = Image.asset(
47 'assets/images/copy_address.png',
48 color: Theme.of(context).colorScheme.onSurfaceVariant,
49 );
50
51 // This magic number for wider screen sets the text input focus at center of the inputfield
52 final _width =
53 responsiveLayoutUtil.shouldRenderMobileUI ? MediaQuery.of(context).size.width : 500;
54
55 return Center(
56 child: SingleChildScrollView(
57 child: Column(
58 mainAxisSize: MainAxisSize.min,
59 mainAxisAlignment: MainAxisAlignment.center,
60 crossAxisAlignment: CrossAxisAlignment.center,
61 children: <Widget>[
62 Column(
63 children: [
64 Row(
65 children: <Widget>[
66 Spacer(flex: 3),
67 Observer(
68 builder: (_) => Flexible(
69 flex: 9,
70 child: GestureDetector(
71 onTap: () {
72 BrightnessUtil.changeBrightnessForFunction(
73 () async {
74 await Navigator.pushNamed(
75 context,
76 Routes.fullscreenQR,
77 arguments: QrViewData(
78 embeddedImagePath: addressListViewModel.qrImage,
79 data: addressUri.toString(),
80 heroTag: heroTag,
81 ),
82 );
83 },
84 );
85 },
86 child: Hero(
87 tag: Key(heroTag ?? addressUri.toString()),
88 child: Center(
89 child: Container(
90 padding: EdgeInsets.zero,
91 decoration: BoxDecoration(
92 border: Border(top: BorderSide.none),
93 borderRadius: BorderRadius.all(Radius.circular(5)),
94 color: Theme.of(context).colorScheme.surface,
95 ),
96 child: Column(
97 children: [
98 ClipRRect(
99 borderRadius: BorderRadius.circular(12.5),
100 child: AspectRatio(
101 aspectRatio: 1.0,
102 child: QrImage(
103 embeddedImagePath: addressListViewModel.qrImage,
104 data: addressUri.toString(),
105 size: 230,
106 ),
107 ),
108 ),
109 if (addressListViewModel.isPayjoinUnavailable &&
110 !addressListViewModel.isSilentPayments &&
111 !addressListViewModel.isLightning &&
112 !addressListViewModel.isBitcoinViewOnly) ...[
113 GestureDetector(
114 onTap: () => _onPayjoinInactivePressed(context),
115 child: Row(
116 mainAxisAlignment: MainAxisAlignment.center,
117 children: [
118 Padding(
119 padding: EdgeInsets.only(
120 top: 4,
121 bottom: 4,
122 right: 4,
123 ),
124 child: Image.asset(
125 'assets/images/payjoin.png',
126 width: 20,
127 ),
128 ),
129 Text(
130 S.of(context).payjoin_unavailable,
131 style:
132 Theme.of(context).textTheme.bodySmall?.copyWith(
133 fontWeight: FontWeight.w600,
134 color: Theme.of(context)
135 .colorScheme
136 .onSurfaceVariant,
137 ),
138 ),
139 Padding(
140 padding: EdgeInsets.only(
141 top: 4,
142 bottom: 4,
143 left: 4,
144 ),
145 child: CircleAvatar(
146 radius: 7,
147 backgroundColor: Colors.black,
148 child: Icon(
149 Icons.question_mark,
150 size: 10,
151 color: Colors.white,
152 ),
153 ),
154 ),
155 ],
156 ),
157 ),
158 ],
159 if (addressListViewModel.isPayjoinAvailable) ...[
160 Row(
161 mainAxisAlignment: MainAxisAlignment.center,
162 children: [
163 Padding(
164 padding: EdgeInsets.only(
165 top: 4,
166 bottom: 4,
167 right: 4,
168 ),
169 child: Image.asset(
170 'assets/images/payjoin.png',
171 width: 20,
172 ),
173 ),
174 Text(
175 S.of(context).payjoin_enabled,
176 style: Theme.of(context).textTheme.bodySmall?.copyWith(
177 fontWeight: FontWeight.w600,
178 ),
179 ),
180 ],
181 ),
182 ]
183 ],
184 ),
185 ),
186 ),
187 ),
188 ),
189 ),
190 ),
191 Spacer(flex: 3)
192 ],
193 ),
194 ],
195 ),
196 Observer(
197 builder: (_) => Row(
198 children: <Widget>[
199 Expanded(
200 child: Form(
201 key: formKey,
202 child: CurrencyAmountTextField(
203 hasUnderlineBorder: true,
204 borderWidth: 0.0,
205 selectedCurrency: _currencyName,
206 selectedCurrencyDecimals:
207 addressListViewModel.selectedCurrencyDecimals,
208 amountFocusNode: amountTextFieldFocusNode,
209 amountController: amountController,
210 padding: EdgeInsets.only(top: 20, left: _width / 4),
211 isAmountEditable: true,
212 tag: addressListViewModel.selectedCurrency.tag,
213 onTapPicker: () => _presentPicker(context),
214 isPickerEnable: true,
215 ),
216 ),
217 ),
218 ],
219 )),
220 Divider(height: 1, color: Theme.of(context).colorScheme.outlineVariant),
221 Padding(
222 padding: EdgeInsets.only(top: 12, bottom: 8),
223 child: Builder(
224 builder: (context) => Observer(
225 builder: (context) => GestureDetector(
226 onTap: () {
227 Clipboard.setData(ClipboardData(text: addressUri.address));
228 showBar<void>(context, S.of(context).copied_to_clipboard);
229 },
230 child: Row(
231 mainAxisSize: MainAxisSize.max,
232 crossAxisAlignment: CrossAxisAlignment.start,
233 children: <Widget>[
234 Expanded(
235 child: AddressFormatter.buildSegmentedAddress(
236 address: addressUri.address,
237 walletType: addressListViewModel.type,
238 textAlign: TextAlign.center,
239 evenTextStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
240 fontSize: 15,
241 fontWeight: FontWeight.w500,
242 color: Theme.of(context).colorScheme.onSurface,
243 ),
244 ),
245 ),
246 Padding(
247 padding: EdgeInsets.only(left: 12),
248 child: copyImage,
249 )
250 ],
251 ),
252 ),
253 ),
254 ),
255 ),
256 Observer(
257 builder: (_) => Offstage(
258 offstage: !addressListViewModel.isPayjoinAvailable,
259 child: Padding(
260 padding: EdgeInsets.only(top: 12),
261 child: PrimaryImageButton(
262 onPressed: () {
263 Clipboard.setData(ClipboardData(text: addressUri.toString()));
264 showBar<void>(context, S.of(context).copied_to_clipboard);
265 },
266 image: Image.asset(
267 'assets/images/payjoin.png',
268 width: 25,
269 ),
270 text: S.of(context).copy_payjoin_address,
271 color: Theme.of(context).colorScheme.surfaceContainer,
272 textColor: Theme.of(context).colorScheme.onSurface,
273 ),
274 ),
275 ),
276 ),
277 ],
278 ),
279 ),
280 );
281 }
282
283 String get _currencyName {
284 if (addressListViewModel.selectedCurrency is CryptoCurrency) {
285 if (addressListViewModel.useSatoshi) return "SATS";
286 return (addressListViewModel.selectedCurrency as CryptoCurrency).title.toUpperCase();
287 }
288 return addressListViewModel.selectedCurrency.name.toUpperCase();
289 }
290
291 void _presentPicker(BuildContext context) async {
292 await showPopUp<void>(
293 builder: (_) => CurrencyPicker(
294 selectedAtIndex: addressListViewModel.selectedCurrencyIndex,
295 items: addressListViewModel.currencies,
296 hintText: S.of(context).search_currency,
297 onItemSelected: addressListViewModel.selectCurrency,
298 ),
299 context: context,
300 );
301 // update amount if currency changed
302 addressListViewModel.changeAmount(amountController.text);
303 }
304
305 void _onPayjoinInactivePressed(BuildContext context) {
306 showModalBottomSheet(
307 context: context,
308 builder: (context) => InfoBottomSheet(
309 titleText: S.of(context).payjoin_unavailable_sheet_title,
310 content: S.of(context).payjoin_unavailable_sheet_content,
311 footerType: FooterType.doubleActionButton,
312 doubleActionLeftButtonText: S.of(context).learn_more,
313 onLeftActionButtonPressed: () => launchUrl(
314 Uri.parse("https://docs.cakewallet.com/cryptos/bitcoin/#payjoin"),
315 mode: LaunchMode.externalApplication),
316 doubleActionRightButtonText: S.of(context).ok,
317 onRightActionButtonPressed: () => Navigator.of(context).pop(),
318 ),
319 );
320 }
321 }