Skip to content

Commit 77c6e94

Browse files
committed
ui(spl): mobile send and receive flows
some TODOs remain, see especially the send view
1 parent 234af02 commit 77c6e94

File tree

4 files changed

+1498
-10
lines changed

4 files changed

+1498
-10
lines changed
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
/*
2+
* This file is part of Stack Wallet.
3+
*
4+
* Copyright (c) 2025 Cypher Stack
5+
* All Rights Reserved.
6+
* The code is distributed under GPLv3 license, see LICENSE file for details.
7+
*
8+
*/
9+
10+
import 'dart:async';
11+
12+
import 'package:flutter/material.dart';
13+
import 'package:flutter/services.dart';
14+
import 'package:flutter_riverpod/flutter_riverpod.dart';
15+
import 'package:flutter_svg/flutter_svg.dart';
16+
17+
import '../../models/isar/models/isar_models.dart';
18+
import '../../notifications/show_flush_bar.dart';
19+
import '../../providers/providers.dart';
20+
import '../../themes/stack_colors.dart';
21+
import '../../utilities/assets.dart';
22+
import '../../utilities/clipboard_interface.dart';
23+
import '../../utilities/constants.dart';
24+
import '../../utilities/text_styles.dart';
25+
import '../../wallets/isar/providers/solana/current_sol_token_wallet_provider.dart';
26+
import '../../wallets/isar/providers/wallet_info_provider.dart';
27+
import '../../widgets/background.dart';
28+
import '../../widgets/custom_buttons/app_bar_icon_button.dart';
29+
import '../../widgets/icon_widgets/sol_token_icon.dart';
30+
import '../../widgets/qr.dart';
31+
import '../../widgets/rounded_white_container.dart';
32+
33+
class SolTokenReceiveView extends ConsumerStatefulWidget {
34+
const SolTokenReceiveView({
35+
super.key,
36+
required this.walletId,
37+
required this.tokenMint,
38+
this.clipboard = const ClipboardWrapper(),
39+
});
40+
41+
static const String routeName = "/solTokenReceiveView";
42+
43+
final String walletId;
44+
final String tokenMint;
45+
final ClipboardInterface clipboard;
46+
47+
@override
48+
ConsumerState<SolTokenReceiveView> createState() =>
49+
_SolTokenReceiveViewState();
50+
}
51+
52+
class _SolTokenReceiveViewState extends ConsumerState<SolTokenReceiveView> {
53+
late final String walletId;
54+
late final String tokenMint;
55+
late final ClipboardInterface clipboard;
56+
57+
@override
58+
void initState() {
59+
walletId = widget.walletId;
60+
tokenMint = widget.tokenMint;
61+
clipboard = widget.clipboard;
62+
super.initState();
63+
}
64+
65+
@override
66+
Widget build(BuildContext context) {
67+
debugPrint("BUILD: $runtimeType");
68+
69+
final tokenWallet = ref.watch(pCurrentSolanaTokenWallet);
70+
final walletName = ref.watch(pWalletName(walletId));
71+
final receivingAddress = ref.watch(pWalletReceivingAddress(walletId));
72+
73+
return Background(
74+
child: Scaffold(
75+
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
76+
appBar: AppBar(
77+
leading: AppBarBackButton(
78+
onPressed: () {
79+
Navigator.of(context).pop();
80+
},
81+
),
82+
title: Text(
83+
tokenWallet != null
84+
? "Receive ${tokenWallet.tokenSymbol}"
85+
: "Receive Token",
86+
style: STextStyles.navBarTitle(context),
87+
),
88+
),
89+
body: SafeArea(
90+
child: SingleChildScrollView(
91+
child: Padding(
92+
padding: const EdgeInsets.all(16),
93+
child: Column(
94+
crossAxisAlignment: CrossAxisAlignment.stretch,
95+
children: [
96+
const SizedBox(height: 12),
97+
Text(
98+
"Your Solana address",
99+
style: STextStyles.itemSubtitle(context),
100+
),
101+
const SizedBox(height: 12),
102+
RoundedWhiteContainer(
103+
padding: const EdgeInsets.all(24),
104+
child: Column(
105+
crossAxisAlignment: CrossAxisAlignment.stretch,
106+
children: [
107+
Center(
108+
child: SizedBox(
109+
width: 200,
110+
height: 200,
111+
child: QR(
112+
data: receivingAddress,
113+
size: 200,
114+
),
115+
),
116+
),
117+
const SizedBox(height: 24),
118+
Container(
119+
decoration: BoxDecoration(
120+
color: Theme.of(
121+
context,
122+
).extension<StackColors>()!.popupBG,
123+
borderRadius: BorderRadius.circular(
124+
Constants.size.circularBorderRadius,
125+
),
126+
),
127+
child: Padding(
128+
padding: const EdgeInsets.all(12.0),
129+
child: Row(
130+
children: [
131+
if (tokenWallet != null)
132+
SolTokenIcon(
133+
mintAddress: tokenMint,
134+
)
135+
else
136+
SizedBox.square(dimension: 32),
137+
const SizedBox(width: 6),
138+
Expanded(
139+
child: Column(
140+
crossAxisAlignment:
141+
CrossAxisAlignment.start,
142+
children: [
143+
Text(
144+
walletName,
145+
style: STextStyles.titleBold12(
146+
context,
147+
).copyWith(fontSize: 14),
148+
overflow: TextOverflow.ellipsis,
149+
maxLines: 1,
150+
),
151+
Text(
152+
"Solana wallet",
153+
style: STextStyles.label(
154+
context,
155+
).copyWith(fontSize: 10),
156+
),
157+
],
158+
),
159+
),
160+
],
161+
),
162+
),
163+
),
164+
const SizedBox(height: 16),
165+
Row(
166+
children: [
167+
Expanded(
168+
child: GestureDetector(
169+
onTap: () async {
170+
await clipboard.setData(
171+
ClipboardData(text: receivingAddress),
172+
);
173+
if (mounted) {
174+
showFloatingFlushBar(
175+
type: FlushBarType.info,
176+
message: "Address copied",
177+
context: context,
178+
);
179+
}
180+
},
181+
child: Container(
182+
padding: const EdgeInsets.symmetric(
183+
horizontal: 12,
184+
vertical: 8,
185+
),
186+
decoration: BoxDecoration(
187+
color: Theme.of(
188+
context,
189+
).extension<StackColors>()!.highlight,
190+
borderRadius: BorderRadius.circular(
191+
Constants.size.circularBorderRadius,
192+
),
193+
),
194+
child: Row(
195+
mainAxisAlignment: MainAxisAlignment.center,
196+
children: [
197+
SvgPicture.asset(
198+
Assets.svg.copy,
199+
width: 16,
200+
height: 16,
201+
colorFilter: ColorFilter.mode(
202+
Theme.of(
203+
context,
204+
).extension<StackColors>()!.textDark,
205+
BlendMode.srcIn,
206+
),
207+
),
208+
const SizedBox(width: 8),
209+
Text(
210+
"Copy",
211+
style:
212+
STextStyles.smallMed12(context)
213+
.copyWith(
214+
color: Theme.of(
215+
context,
216+
).extension<StackColors>()!
217+
.textDark,
218+
),
219+
),
220+
],
221+
),
222+
),
223+
),
224+
),
225+
],
226+
),
227+
],
228+
),
229+
),
230+
const SizedBox(height: 24),
231+
Text(
232+
"Address",
233+
style: STextStyles.itemSubtitle(context),
234+
),
235+
const SizedBox(height: 8),
236+
Container(
237+
decoration: BoxDecoration(
238+
color: Theme.of(
239+
context,
240+
).extension<StackColors>()!.popupBG,
241+
borderRadius: BorderRadius.circular(
242+
Constants.size.circularBorderRadius,
243+
),
244+
),
245+
child: Padding(
246+
padding: const EdgeInsets.all(12),
247+
child: SelectableText(
248+
receivingAddress,
249+
style: STextStyles.label(context),
250+
),
251+
),
252+
),
253+
const SizedBox(height: 24),
254+
],
255+
),
256+
),
257+
),
258+
),
259+
),
260+
);
261+
}
262+
}

0 commit comments

Comments
 (0)