coin control select for sending

This commit is contained in:
julian 2023-03-07 15:45:22 -06:00
parent 35c17033d1
commit 6d22304d7b
4 changed files with 448 additions and 328 deletions

View file

@ -15,6 +15,7 @@ import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart'; import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/toggle.dart'; import 'package:stackwallet/widgets/toggle.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
@ -30,6 +31,7 @@ class CoinControlView extends ConsumerStatefulWidget {
required this.walletId, required this.walletId,
required this.type, required this.type,
this.requestedTotal, this.requestedTotal,
this.selectedUTXOs,
}) : super(key: key); }) : super(key: key);
static const routeName = "/coinControl"; static const routeName = "/coinControl";
@ -37,6 +39,7 @@ class CoinControlView extends ConsumerStatefulWidget {
final String walletId; final String walletId;
final CoinControlViewType type; final CoinControlViewType type;
final int? requestedTotal; final int? requestedTotal;
final Set<UTXO>? selectedUTXOs;
@override @override
ConsumerState<CoinControlView> createState() => _CoinControlViewState(); ConsumerState<CoinControlView> createState() => _CoinControlViewState();
@ -48,6 +51,14 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
final Set<UTXO> _selectedAvailable = {}; final Set<UTXO> _selectedAvailable = {};
final Set<UTXO> _selectedBlocked = {}; final Set<UTXO> _selectedBlocked = {};
@override
void initState() {
if (widget.selectedUTXOs != null) {
_selectedAvailable.addAll(widget.selectedUTXOs!);
}
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType"); debugPrint("BUILD: $runtimeType");
@ -69,300 +80,326 @@ class _CoinControlViewState extends ConsumerState<CoinControlView> {
.idProperty() .idProperty()
.findAllSync(); .findAllSync();
return Background( return WillPopScope(
child: Scaffold( onWillPop: () async {
backgroundColor: Theme.of(context).extension<StackColors>()!.background, Navigator.of(context).pop(
appBar: AppBar( widget.type == CoinControlViewType.use ? _selectedAvailable : null);
leading: AppBarBackButton( return false;
onPressed: () { },
Navigator.of(context).pop(); child: Background(
}, child: Scaffold(
), backgroundColor:
title: Text( Theme.of(context).extension<StackColors>()!.background,
"Coin control", appBar: AppBar(
style: STextStyles.navBarTitle(context), leading: widget.type == CoinControlViewType.use &&
), _selectedAvailable.isNotEmpty
titleSpacing: 0, ? AppBarIconButton(
), icon: XIcon(
body: SafeArea( width: 24,
child: Column( height: 24,
children: [ color: Theme.of(context)
Expanded( .extension<StackColors>()!
child: Padding( .topNavIconPrimary,
padding: const EdgeInsets.symmetric( ),
horizontal: 16, onPressed: () {
setState(() {
_selectedAvailable.clear();
});
},
)
: AppBarBackButton(
onPressed: () {
Navigator.of(context).pop(
widget.type == CoinControlViewType.use
? _selectedAvailable
: null);
},
), ),
child: Column( title: Text(
children: [ "Coin control",
const SizedBox( style: STextStyles.navBarTitle(context),
height: 10, ),
), titleSpacing: 0,
RoundedWhiteContainer( ),
child: Text( body: SafeArea(
"This option allows you to control, freeze, and utilize " child: Column(
"outputs at your discretion. Tap the output circle to " children: [
"select.", Expanded(
style: STextStyles.w500_14(context).copyWith( child: Padding(
color: Theme.of(context) padding: const EdgeInsets.symmetric(
.extension<StackColors>()! horizontal: 16,
.textSubtitle1, ),
), child: Column(
children: [
const SizedBox(
height: 10,
), ),
), RoundedWhiteContainer(
const SizedBox( child: Text(
height: 10, "This option allows you to control, freeze, and utilize "
), "outputs at your discretion. Tap the output circle to "
SizedBox( "select.",
height: 48, style: STextStyles.w500_14(context).copyWith(
child: Toggle( color: Theme.of(context)
key: UniqueKey(), .extension<StackColors>()!
onColor: Theme.of(context) .textSubtitle1,
.extension<StackColors>()!
.popupBG,
onText: "Available outputs",
offColor: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
offText: "Frozen outputs",
isOn: _showBlocked,
onValueChanged: (value) {
setState(() {
_showBlocked = value;
});
},
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
), ),
), ),
), ),
), const SizedBox(
const SizedBox( height: 10,
height: 10,
),
Expanded(
child: ListView.separated(
itemCount: ids.length,
separatorBuilder: (context, _) => const SizedBox(
height: 10,
),
itemBuilder: (context, index) {
final utxo = MainDB.instance.isar.utxos
.where()
.idEqualTo(ids[index])
.findFirstSync()!;
return UtxoCard(
key: Key("${utxo.walletId}_${utxo.id}"),
walletId: widget.walletId,
utxo: utxo,
initialSelectedState: _showBlocked
? _selectedBlocked.contains(utxo)
: _selectedAvailable.contains(utxo),
onSelectedChanged: (value) {
if (value) {
_showBlocked
? _selectedBlocked.add(utxo)
: _selectedAvailable.add(utxo);
} else {
_showBlocked
? _selectedBlocked.remove(utxo)
: _selectedAvailable.remove(utxo);
}
setState(() {});
},
onPressed: () async {
final result =
await Navigator.of(context).pushNamed(
UtxoDetailsView.routeName,
arguments: Tuple2(
utxo.id,
widget.walletId,
),
);
if (mounted && result == "refresh") {
setState(() {});
}
},
);
},
), ),
), SizedBox(
], height: 48,
), child: Toggle(
), key: UniqueKey(),
), onColor: Theme.of(context)
if (((_showBlocked && _selectedBlocked.isNotEmpty) || .extension<StackColors>()!
(!_showBlocked && _selectedAvailable.isNotEmpty)) && .popupBG,
widget.type == CoinControlViewType.manage) onText: "Available outputs",
Container( offColor: Theme.of(context)
decoration: BoxDecoration( .extension<StackColors>()!
color: Theme.of(context) .textFieldDefaultBG,
.extension<StackColors>()! offText: "Frozen outputs",
.backgroundAppBar, isOn: _showBlocked,
boxShadow: [ onValueChanged: (value) {
Theme.of(context) setState(() {
.extension<StackColors>()! _showBlocked = value;
.standardBoxShadow, });
], },
), decoration: BoxDecoration(
child: Padding( color: Colors.transparent,
padding: const EdgeInsets.all(16), borderRadius: BorderRadius.circular(
child: SecondaryButton( Constants.size.circularBorderRadius,
label: _showBlocked ? "Unfreeze" : "Freeze",
onPressed: () async {
if (_showBlocked) {
await MainDB.instance.putUTXOs(_selectedBlocked
.map(
(e) => e.copyWith(
isBlocked: false,
),
)
.toList());
_selectedBlocked.clear();
} else {
await MainDB.instance.putUTXOs(_selectedAvailable
.map(
(e) => e.copyWith(
isBlocked: true,
),
)
.toList());
_selectedAvailable.clear();
}
setState(() {});
},
),
),
),
if (!_showBlocked && widget.type == CoinControlViewType.use)
Container(
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
boxShadow: [
Theme.of(context)
.extension<StackColors>()!
.standardBoxShadow,
],
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
RoundedWhiteContainer(
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Selected amount",
style: STextStyles.w600_14(context),
),
Builder(builder: (context) {
int selectedSum = _selectedAvailable
.map((e) => e.value)
.reduce(
(value, element) => value += element,
);
return Text(
"${Format.satoshisToAmount(
selectedSum,
coin: coin,
).toStringAsFixed(
coin.decimals,
)} ${coin.ticker}",
style: widget.requestedTotal == null
? STextStyles.w600_14(context)
: STextStyles.w600_14(context)
.copyWith(
color: selectedSum >=
widget.requestedTotal!
? Theme.of(context)
.extension<
StackColors>()!
.accentColorGreen
: Theme.of(context)
.extension<
StackColors>()!
.accentColorRed),
);
}),
],
), ),
if (widget.requestedTotal != null) ),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 12,
),
child: Container(
width: double.infinity,
height: 2,
color: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
),
),
if (widget.requestedTotal != null)
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Amount to send",
style: STextStyles.w600_14(context),
),
Text(
"${Format.satoshisToAmount(
widget.requestedTotal!,
coin: coin,
).toStringAsFixed(
coin.decimals,
)} ${coin.ticker}",
style: STextStyles.w600_14(context),
),
],
),
],
), ),
), ),
const SizedBox( const SizedBox(
height: 12, height: 10,
), ),
PrimaryButton( Expanded(
label: "Use coins", child: ListView.separated(
onPressed: () async { itemCount: ids.length,
if (_showBlocked) { separatorBuilder: (context, _) => const SizedBox(
await MainDB.instance.putUTXOs(_selectedBlocked height: 10,
.map( ),
(e) => e.copyWith( itemBuilder: (context, index) {
isBlocked: false, final utxo = MainDB.instance.isar.utxos
.where()
.idEqualTo(ids[index])
.findFirstSync()!;
final isSelected = _showBlocked
? _selectedBlocked.contains(utxo)
: _selectedAvailable.contains(utxo);
return UtxoCard(
key: Key(
"${utxo.walletId}_${utxo.id}_$isSelected"),
walletId: widget.walletId,
utxo: utxo,
canSelect: widget.type ==
CoinControlViewType.manage ||
(widget.type == CoinControlViewType.use &&
!_showBlocked),
initialSelectedState: isSelected,
onSelectedChanged: (value) {
if (value) {
_showBlocked
? _selectedBlocked.add(utxo)
: _selectedAvailable.add(utxo);
} else {
_showBlocked
? _selectedBlocked.remove(utxo)
: _selectedAvailable.remove(utxo);
}
setState(() {});
},
onPressed: () async {
final result =
await Navigator.of(context).pushNamed(
UtxoDetailsView.routeName,
arguments: Tuple2(
utxo.id,
widget.walletId,
), ),
) );
.toList()); if (mounted && result == "refresh") {
_selectedBlocked.clear(); setState(() {});
} else { }
await MainDB.instance.putUTXOs(_selectedAvailable },
.map( );
(e) => e.copyWith( },
isBlocked: true, ),
),
)
.toList());
_selectedAvailable.clear();
}
setState(() {});
},
), ),
], ],
), ),
), ),
), ),
], if (((_showBlocked && _selectedBlocked.isNotEmpty) ||
(!_showBlocked && _selectedAvailable.isNotEmpty)) &&
widget.type == CoinControlViewType.manage)
Container(
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
boxShadow: [
Theme.of(context)
.extension<StackColors>()!
.standardBoxShadow,
],
),
child: Padding(
padding: const EdgeInsets.all(16),
child: SecondaryButton(
label: _showBlocked ? "Unfreeze" : "Freeze",
onPressed: () async {
if (_showBlocked) {
await MainDB.instance.putUTXOs(_selectedBlocked
.map(
(e) => e.copyWith(
isBlocked: false,
),
)
.toList());
_selectedBlocked.clear();
} else {
await MainDB.instance.putUTXOs(_selectedAvailable
.map(
(e) => e.copyWith(
isBlocked: true,
),
)
.toList());
_selectedAvailable.clear();
}
setState(() {});
},
),
),
),
if (!_showBlocked && widget.type == CoinControlViewType.use)
Container(
decoration: BoxDecoration(
color: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
boxShadow: [
Theme.of(context)
.extension<StackColors>()!
.standardBoxShadow,
],
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(12),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Selected amount",
style: STextStyles.w600_14(context),
),
Builder(
builder: (context) {
int selectedSum =
_selectedAvailable.isEmpty
? 0
: _selectedAvailable
.map((e) => e.value)
.reduce(
(value, element) =>
value += element,
);
return Text(
"${Format.satoshisToAmount(
selectedSum,
coin: coin,
).toStringAsFixed(
coin.decimals,
)} ${coin.ticker}",
style: widget.requestedTotal == null
? STextStyles.w600_14(context)
: STextStyles.w600_14(context).copyWith(
color: selectedSum >=
widget
.requestedTotal!
? Theme.of(context)
.extension<
StackColors>()!
.accentColorGreen
: Theme.of(context)
.extension<
StackColors>()!
.accentColorRed),
);
},
),
],
),
),
if (widget.requestedTotal != null)
Container(
width: double.infinity,
height: 1.5,
color: Theme.of(context)
.extension<StackColors>()!
.backgroundAppBar,
),
if (widget.requestedTotal != null)
Padding(
padding: const EdgeInsets.all(12),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Amount to send",
style: STextStyles.w600_14(context),
),
Text(
"${Format.satoshisToAmount(
widget.requestedTotal!,
coin: coin,
).toStringAsFixed(
coin.decimals,
)} ${coin.ticker}",
style: STextStyles.w600_14(context),
),
],
),
),
],
),
),
const SizedBox(
height: 12,
),
PrimaryButton(
label: "Use coins",
enabled: _selectedAvailable.isNotEmpty,
onPressed: () async {
Navigator.of(context).pop(
_selectedAvailable,
);
},
),
],
),
),
),
],
),
), ),
), ),
), ),

View file

@ -21,6 +21,7 @@ class UtxoCard extends ConsumerStatefulWidget {
required this.walletId, required this.walletId,
required this.onSelectedChanged, required this.onSelectedChanged,
required this.initialSelectedState, required this.initialSelectedState,
required this.canSelect,
this.onPressed, this.onPressed,
}) : super(key: key); }) : super(key: key);
@ -29,6 +30,7 @@ class UtxoCard extends ConsumerStatefulWidget {
final void Function(bool) onSelectedChanged; final void Function(bool) onSelectedChanged;
final bool initialSelectedState; final bool initialSelectedState;
final VoidCallback? onPressed; final VoidCallback? onPressed;
final bool canSelect;
@override @override
ConsumerState<UtxoCard> createState() => _UtxoCardState(); ConsumerState<UtxoCard> createState() => _UtxoCardState();
@ -90,12 +92,16 @@ class _UtxoCardState extends ConsumerState<UtxoCard> {
: Colors.transparent, : Colors.transparent,
child: Row( child: Row(
children: [ children: [
GestureDetector( ConditionalParent(
onTap: () { condition: widget.canSelect,
_selected = !_selected; builder: (child) => GestureDetector(
widget.onSelectedChanged(_selected); onTap: () {
setState(() {}); _selected = !_selected;
}, widget.onSelectedChanged(_selected);
setState(() {});
},
child: child,
),
child: SvgPicture.asset( child: SvgPicture.asset(
_selected _selected
? Assets.svg.coinControl.selected ? Assets.svg.coinControl.selected

View file

@ -7,9 +7,11 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/models/paynym/paynym_account_lite.dart'; import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
import 'package:stackwallet/models/send_view_auto_fill_data.dart'; import 'package:stackwallet/models/send_view_auto_fill_data.dart';
import 'package:stackwallet/pages/address_book_views/address_book_view.dart'; import 'package:stackwallet/pages/address_book_views/address_book_view.dart';
import 'package:stackwallet/pages/coin_control/coin_control_view.dart';
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart'; import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart'; import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart';
import 'package:stackwallet/pages/send_view/sub_widgets/firo_balance_selection_sheet.dart'; import 'package:stackwallet/pages/send_view/sub_widgets/firo_balance_selection_sheet.dart';
@ -43,9 +45,11 @@ import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart'; import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart'; import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/stack_text_field.dart'; import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:stackwallet/widgets/textfield_icon_button.dart'; import 'package:stackwallet/widgets/textfield_icon_button.dart';
import 'package:tuple/tuple.dart';
class SendView extends ConsumerStatefulWidget { class SendView extends ConsumerStatefulWidget {
const SendView({ const SendView({
@ -104,6 +108,8 @@ class _SendViewState extends ConsumerState<SendView> {
Decimal? _cachedBalance; Decimal? _cachedBalance;
Set<UTXO> selectedUTXOs = {};
void _cryptoAmountChanged() async { void _cryptoAmountChanged() async {
if (!_cryptoAmountChangeLock) { if (!_cryptoAmountChangeLock) {
final String cryptoAmount = cryptoAmountController.text; final String cryptoAmount = cryptoAmountController.text;
@ -313,51 +319,54 @@ class _SendViewState extends ConsumerState<SendView> {
Format.decimalAmountToSatoshis(manager.balance.getSpendable(), coin); Format.decimalAmountToSatoshis(manager.balance.getSpendable(), coin);
} }
// confirm send all if (!manager.hasCoinControlSupport ||
if (amount == availableBalance) { (manager.hasCoinControlSupport && selectedUTXOs.isEmpty)) {
final bool? shouldSendAll = await showDialog<bool>( // confirm send all
context: context, if (amount == availableBalance) {
useSafeArea: false, final bool? shouldSendAll = await showDialog<bool>(
barrierDismissible: true, context: context,
builder: (context) { useSafeArea: false,
return StackDialog( barrierDismissible: true,
title: "Confirm send all", builder: (context) {
message: return StackDialog(
"You are about to send your entire balance. Would you like to continue?", title: "Confirm send all",
leftButton: TextButton( message:
style: Theme.of(context) "You are about to send your entire balance. Would you like to continue?",
.extension<StackColors>()! leftButton: TextButton(
.getSecondaryEnabledButtonStyle(context), style: Theme.of(context)
child: Text( .extension<StackColors>()!
"Cancel", .getSecondaryEnabledButtonStyle(context),
style: STextStyles.button(context).copyWith( child: Text(
color: Theme.of(context) "Cancel",
.extension<StackColors>()! style: STextStyles.button(context).copyWith(
.accentColorDark), color: Theme.of(context)
.extension<StackColors>()!
.accentColorDark),
),
onPressed: () {
Navigator.of(context).pop(false);
},
), ),
onPressed: () { rightButton: TextButton(
Navigator.of(context).pop(false); style: Theme.of(context)
}, .extension<StackColors>()!
), .getPrimaryEnabledButtonStyle(context),
rightButton: TextButton( child: Text(
style: Theme.of(context) "Yes",
.extension<StackColors>()! style: STextStyles.button(context),
.getPrimaryEnabledButtonStyle(context), ),
child: Text( onPressed: () {
"Yes", Navigator.of(context).pop(true);
style: STextStyles.button(context), },
), ),
onPressed: () { );
Navigator.of(context).pop(true); },
}, );
),
);
},
);
if (shouldSendAll == null || shouldSendAll == false) { if (shouldSendAll == null || shouldSendAll == false) {
// cancel preview // cancel preview
return; return;
}
} }
} }
@ -393,7 +402,12 @@ class _SendViewState extends ConsumerState<SendView> {
txData = await wallet.preparePaymentCodeSend( txData = await wallet.preparePaymentCodeSend(
paymentCode: paymentCode, paymentCode: paymentCode,
satoshiAmount: amount, satoshiAmount: amount,
args: {"feeRate": feeRate}, args: {
"feeRate": feeRate,
"UTXOs": (manager.hasCoinControlSupport && selectedUTXOs.isNotEmpty)
? selectedUTXOs
: null,
},
); );
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) && } else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
ref.read(publicPrivateBalanceStateProvider.state).state != ref.read(publicPrivateBalanceStateProvider.state).state !=
@ -407,7 +421,12 @@ class _SendViewState extends ConsumerState<SendView> {
txData = await manager.prepareSend( txData = await manager.prepareSend(
address: _address!, address: _address!,
satoshiAmount: amount, satoshiAmount: amount,
args: {"feeRate": ref.read(feeRateTypeStateProvider)}, args: {
"feeRate": ref.read(feeRateTypeStateProvider),
"UTXOs": (manager.hasCoinControlSupport && selectedUTXOs.isNotEmpty)
? selectedUTXOs
: null,
},
); );
} }
@ -565,6 +584,12 @@ class _SendViewState extends ConsumerState<SendView> {
final String locale = ref.watch( final String locale = ref.watch(
localeServiceChangeNotifierProvider.select((value) => value.locale)); localeServiceChangeNotifierProvider.select((value) => value.locale));
final showCoinControl = ref.watch(
walletsChangeNotifierProvider.select(
(value) => value.getManager(walletId).hasCoinControlSupport,
),
);
if (coin == Coin.firo || coin == Coin.firoTestNet) { if (coin == Coin.firo || coin == Coin.firoTestNet) {
ref.listen(publicPrivateBalanceStateProvider, (previous, next) { ref.listen(publicPrivateBalanceStateProvider, (previous, next) {
if (_amountToSend == null) { if (_amountToSend == null) {
@ -1484,6 +1509,56 @@ class _SendViewState extends ConsumerState<SendView> {
), ),
), ),
), ),
if (showCoinControl)
const SizedBox(
height: 8,
),
if (showCoinControl)
RoundedWhiteContainer(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Coin control",
style:
STextStyles.w500_14(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
CustomTextButton(
text: selectedUTXOs.isEmpty
? "Select coins"
: "Selected coins (${selectedUTXOs.length})",
onTap: () async {
final result =
await Navigator.of(context).pushNamed(
CoinControlView.routeName,
arguments: Tuple4(
walletId,
CoinControlViewType.use,
_amountToSend != null
? Format.decimalAmountToSatoshis(
_amountToSend!,
coin,
)
: null,
selectedUTXOs,
),
);
if (result is Set<UTXO>) {
setState(() {
selectedUTXOs = result;
});
}
},
),
],
),
),
const SizedBox( const SizedBox(
height: 12, height: 12,
), ),

View file

@ -229,13 +229,15 @@ class RouteGenerator {
name: settings.name, name: settings.name,
), ),
); );
} else if (args is Tuple3<String, CoinControlViewType, int?>) { } else if (args
is Tuple4<String, CoinControlViewType, int?, Set<UTXO>?>) {
return getRoute( return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute, shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => CoinControlView( builder: (_) => CoinControlView(
walletId: args.item1, walletId: args.item1,
type: args.item2, type: args.item2,
requestedTotal: args.item3, requestedTotal: args.item3,
selectedUTXOs: args.item4,
), ),
settings: RouteSettings( settings: RouteSettings(
name: settings.name, name: settings.name,