dev
dart 156 lines 7.05 KB
Raw
1 import 'dart:math';
2
3 import 'package:cake_wallet/di.dart';
4 import 'package:cake_wallet/generated/i18n.dart';
5 import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
6 import 'package:cake_wallet/themes/core/theme_store.dart';
7 import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
8 import 'package:cw_core/crypto_currency.dart';
9 import 'package:flutter/material.dart';
10 import 'package:flutter_mobx/flutter_mobx.dart';
11
12 import '../../../src/screens/receive/widgets/qr_image.dart';
13
14 class ReceiveQrCode extends StatelessWidget {
15 ReceiveQrCode({
16 super.key,
17 required this.onTap,
18 required this.largeQrMode,
19 required this.addressListViewModel,
20 });
21
22 final VoidCallback onTap;
23 final bool largeQrMode;
24 final WalletAddressListViewModel addressListViewModel;
25
26 static const double largeQrModeBottomPadding = 140;
27 static const Duration animDuration = Duration(milliseconds: 500);
28 final bool isLightMode = !(getIt.get<ThemeStore>().currentTheme.isDark);
29
30 @override
31 Widget build(BuildContext context) {
32 final double targetY = largeQrMode ? largeQrModeBottomPadding + 50 : 0;
33 final double resolvedSize =
34 min(MediaQuery.of(context).size.width, MediaQuery.of(context).size.height) * 0.5;
35 final double resolvedScale = largeQrMode ? 1.7 : 1;
36
37 return Stack(
38 alignment: Alignment.topCenter,
39 children: [
40 AnimatedSlide(
41 curve: Curves.easeOutCubic,
42 duration: animDuration,
43 offset: largeQrMode ? Offset(0, -1) : Offset.zero,
44 child: AnimatedOpacity(
45 duration: animDuration,
46 opacity: largeQrMode ? 1 : 0,
47 child: CakeImageWidget(
48 imageUrl: isLightMode
49 ? "assets/new-ui/cakewallet-wordmark-light.svg"
50 : "assets/new-ui/cakewallet-wordmark.svg",
51 height: 45,
52 )),
53 ),
54 // The QR, and the payjoin badge below it, share one tap target, so they
55 // are merged into a single button node named by the QR's own label
56 // rather than each becoming a separate focus stop.
57 MergeSemantics(
58 child: Semantics(
59 hint: largeQrMode ? S.of(context).qr_close_fullscreen : S.of(context).qr_fullscreen,
60 button: true,
61 enabled: true,
62 child: GestureDetector(
63 onTap: onTap,
64 behavior: HitTestBehavior.opaque,
65 child: TweenAnimationBuilder<double>(
66 tween: Tween<double>(begin: 0, end: targetY),
67 duration: animDuration,
68 curve: Curves.easeOutCubic,
69 builder: (context, value, child) {
70 return Transform.translate(
71 offset: Offset(0, value),
72 child: child,
73 );
74 },
75 child: Observer(
76 builder: (_) => Column(
77 children: [
78 AnimatedScale(
79 curve: Curves.easeOutCubic,
80 alignment: Alignment.bottomCenter,
81 scale: resolvedScale,
82 duration: animDuration,
83 child: AnimatedContainer(
84 duration: animDuration,
85 curve: Curves.easeOutCubic,
86 width: resolvedSize,
87 height: resolvedSize,
88 decoration: BoxDecoration(
89 borderRadius: BorderRadius.circular(20),
90 color: Colors.white,
91 ),
92 child: Container(
93 decoration: BoxDecoration(
94 borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
95 color: addressListViewModel.hasPayjoin && !largeQrMode
96 ? Theme.of(context).colorScheme.surfaceContainer
97 : Colors.transparent),
98 child: ClipRRect(
99 borderRadius: BorderRadius.circular(20),
100 child: Observer(
101 builder: (_) => QrImage(
102 data: addressListViewModel.uri.toString(),
103 size: resolvedSize,
104 semanticsLabel: S.of(context).qr_code_receive_address,
105 embeddedImagePath:
106 addressListViewModel.tokenCurrency != null
107 ? addressListViewModel.tokenCurrency ==
108 CryptoCurrency.btcln
109 ? addressListViewModel.qrImage
110 : addressListViewModel
111 .tokenCurrency!.iconPath
112 : addressListViewModel.qrImage,
113 ))),
114 ),
115 ),
116 ),
117 if (addressListViewModel.hasPayjoin)
118 // Hidden but still mounted in large-QR mode, so keep it
119 // out of the semantics tree there.
120 ExcludeSemantics(
121 excluding: largeQrMode,
122 child: Opacity(
123 opacity: largeQrMode ? 0 : 1,
124 child: Container(
125 width: resolvedSize,
126 decoration: BoxDecoration(
127 borderRadius:
128 BorderRadius.vertical(bottom: Radius.circular(16)),
129 color: Theme.of(context).colorScheme.surfaceContainer),
130 child: Padding(
131 padding: const EdgeInsets.symmetric(vertical: 2.0),
132 child: Row(
133 mainAxisAlignment: MainAxisAlignment.center,
134 spacing: 4,
135 children: [
136 CakeImageWidget(imageUrl: "assets/new-ui/payjoin.svg"),
137 Text(S.of(context).payjoin_enabled)
138 ],
139 ),
140 ))),
141 ),
142 AnimatedSize(
143 duration: animDuration,
144 curve: Curves.easeOutCubic,
145 child: SizedBox(height: largeQrMode ? largeQrModeBottomPadding + 40 : 0))
146 ],
147 ),
148 ),
149 ),
150 ),
151 ),
152 ),
153 ],
154 );
155 }
156 }