|
@@ -8,6 +8,7 @@ import '../../../core/l10n/app_localizations.dart';
|
|
|
import '../../../core/theme/app_colors.dart';
|
|
import '../../../core/theme/app_colors.dart';
|
|
|
import '../../../core/utils/dialog_utils.dart' show extractErrorMessage;
|
|
import '../../../core/utils/dialog_utils.dart' show extractErrorMessage;
|
|
|
import '../../../core/utils/top_toast.dart';
|
|
import '../../../core/utils/top_toast.dart';
|
|
|
|
|
+import '../../../data/models/asset/withdraw_coin_option.dart';
|
|
|
import '../../../providers/withdraw_provider.dart';
|
|
import '../../../providers/withdraw_provider.dart';
|
|
|
|
|
|
|
|
class WithdrawScreen extends ConsumerStatefulWidget {
|
|
class WithdrawScreen extends ConsumerStatefulWidget {
|
|
@@ -105,6 +106,33 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Widget _buildBody(BuildContext context, WithdrawState state, WithdrawNotifier notifier) {
|
|
Widget _buildBody(BuildContext context, WithdrawState state, WithdrawNotifier notifier) {
|
|
|
|
|
+ if (state.isLoading) {
|
|
|
|
|
+ return const Center(child: CircularProgressIndicator());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (state.errorMessage != null && state.networkList.isEmpty) {
|
|
|
|
|
+ final l10n = AppLocalizations.of(context)!;
|
|
|
|
|
+ return Center(
|
|
|
|
|
+ child: Padding(
|
|
|
|
|
+ padding: const EdgeInsets.all(24),
|
|
|
|
|
+ child: Column(
|
|
|
|
|
+ mainAxisSize: MainAxisSize.min,
|
|
|
|
|
+ children: [
|
|
|
|
|
+ Text(
|
|
|
|
|
+ resolveProviderError(state.errorMessage, l10n) ??
|
|
|
|
|
+ state.errorMessage!,
|
|
|
|
|
+ textAlign: TextAlign.center,
|
|
|
|
|
+ ),
|
|
|
|
|
+ const SizedBox(height: 16),
|
|
|
|
|
+ FilledButton(
|
|
|
|
|
+ onPressed: notifier.refresh,
|
|
|
|
|
+ child: Text(l10n.retry),
|
|
|
|
|
+ ),
|
|
|
|
|
+ ],
|
|
|
|
|
+ ),
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
|
|
|
|
|
|
|
return Column(
|
|
return Column(
|
|
@@ -245,12 +273,66 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ String _withdrawNetworkTabLabel(WithdrawNetworkItem net) {
|
|
|
|
|
+ final protocol = net.protocol.trim();
|
|
|
|
|
+ final name = net.name.trim();
|
|
|
|
|
+ if (protocol.isEmpty) {
|
|
|
|
|
+ return name;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (name.isEmpty) {
|
|
|
|
|
+ return protocol;
|
|
|
|
|
+ }
|
|
|
|
|
+ return '$name · $protocol';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String _subCoinTabLabel(WithdrawCoinOption coin) {
|
|
|
|
|
+ if (coin.displayName.trim().isNotEmpty) {
|
|
|
|
|
+ return coin.displayName;
|
|
|
|
|
+ }
|
|
|
|
|
+ return coin.unit.toUpperCase();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Widget _buildSelectableChip({
|
|
|
|
|
+ required BuildContext context,
|
|
|
|
|
+ required ColorScheme cs,
|
|
|
|
|
+ required String label,
|
|
|
|
|
+ required bool selected,
|
|
|
|
|
+ required VoidCallback? onTap,
|
|
|
|
|
+ }) {
|
|
|
|
|
+ return Padding(
|
|
|
|
|
+ padding: const EdgeInsets.only(right: 10, bottom: 8),
|
|
|
|
|
+ child: GestureDetector(
|
|
|
|
|
+ onTap: onTap,
|
|
|
|
|
+ child: Container(
|
|
|
|
|
+ padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
|
|
|
|
|
+ decoration: BoxDecoration(
|
|
|
|
|
+ color: selected ? AppColors.brand : Colors.transparent,
|
|
|
|
|
+ border: Border.all(
|
|
|
|
|
+ color: selected ? AppColors.brand : cs.outline.withAlpha(80),
|
|
|
|
|
+ ),
|
|
|
|
|
+ borderRadius: BorderRadius.circular(8),
|
|
|
|
|
+ ),
|
|
|
|
|
+ child: Text(
|
|
|
|
|
+ label,
|
|
|
|
|
+ style: TextStyle(
|
|
|
|
|
+ color: selected ? Colors.black : cs.onSurface,
|
|
|
|
|
+ fontSize: 14,
|
|
|
|
|
+ fontWeight: selected ? FontWeight.w600 : FontWeight.w400,
|
|
|
|
|
+ ),
|
|
|
|
|
+ ),
|
|
|
|
|
+ ),
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
Widget _buildOnChain(BuildContext context, WithdrawState state, WithdrawNotifier notifier) {
|
|
Widget _buildOnChain(BuildContext context, WithdrawState state, WithdrawNotifier notifier) {
|
|
|
final cs = Theme.of(context).colorScheme;
|
|
final cs = Theme.of(context).colorScheme;
|
|
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
|
|
|
|
+ final l10n = AppLocalizations.of(context)!;
|
|
|
final fee = state.fee;
|
|
final fee = state.fee;
|
|
|
final minAmount = state.minWithdrawAmount;
|
|
final minAmount = state.minWithdrawAmount;
|
|
|
final available = state.withdrawableBalance;
|
|
final available = state.withdrawableBalance;
|
|
|
|
|
+ final coinSymbol = state.onchainCoinSymbol;
|
|
|
|
|
|
|
|
// 到账金额计算
|
|
// 到账金额计算
|
|
|
final inputAmount = Decimal.tryParse(_amountController.text) ?? Decimal.zero;
|
|
final inputAmount = Decimal.tryParse(_amountController.text) ?? Decimal.zero;
|
|
@@ -260,16 +342,9 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
return Column(
|
|
return Column(
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
children: [
|
|
children: [
|
|
|
- // 选择币种
|
|
|
|
|
- _Label(AppLocalizations.of(context)!.selectCoin),
|
|
|
|
|
- const SizedBox(height: 8),
|
|
|
|
|
- _FixedCoinField(coin: 'USDT'),
|
|
|
|
|
- const SizedBox(height: 16),
|
|
|
|
|
-
|
|
|
|
|
- // 提币网络
|
|
|
|
|
Row(
|
|
Row(
|
|
|
children: [
|
|
children: [
|
|
|
- _Label(AppLocalizations.of(context)!.withdrawNetwork),
|
|
|
|
|
|
|
+ _Label(l10n.withdrawNetwork),
|
|
|
const SizedBox(width: 4),
|
|
const SizedBox(width: 4),
|
|
|
GestureDetector(
|
|
GestureDetector(
|
|
|
onTap: () => _showNetworkTipDialog(context),
|
|
onTap: () => _showNetworkTipDialog(context),
|
|
@@ -278,38 +353,65 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
],
|
|
],
|
|
|
),
|
|
),
|
|
|
const SizedBox(height: 8),
|
|
const SizedBox(height: 8),
|
|
|
- Row(
|
|
|
|
|
- children: List.generate(state.networkNames.length, (i) {
|
|
|
|
|
- final selected = i == state.selectedNetworkIndex;
|
|
|
|
|
- return Padding(
|
|
|
|
|
- padding: const EdgeInsets.only(right: 10),
|
|
|
|
|
- child: GestureDetector(
|
|
|
|
|
- onTap: () => notifier.selectNetwork(i),
|
|
|
|
|
- child: Container(
|
|
|
|
|
- padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- color: selected ? AppColors.brand : Colors.transparent,
|
|
|
|
|
- border: Border.all(color: selected ? AppColors.brand : cs.outline.withAlpha(80)),
|
|
|
|
|
- borderRadius: BorderRadius.circular(8),
|
|
|
|
|
- ),
|
|
|
|
|
- child: Text(state.networkNames[i], style: TextStyle(
|
|
|
|
|
- color: selected ? Colors.black : cs.onSurface,
|
|
|
|
|
- fontSize: 14,
|
|
|
|
|
- fontWeight: selected ? FontWeight.w600 : FontWeight.w400,
|
|
|
|
|
- )),
|
|
|
|
|
|
|
+ if (state.networkList.isEmpty)
|
|
|
|
|
+ Text(
|
|
|
|
|
+ l10n.depositNoNetwork,
|
|
|
|
|
+ style: TextStyle(color: cs.onSurface.withAlpha(140), fontSize: 13),
|
|
|
|
|
+ )
|
|
|
|
|
+ else
|
|
|
|
|
+ Wrap(
|
|
|
|
|
+ children: [
|
|
|
|
|
+ for (final net in state.networkList)
|
|
|
|
|
+ _buildSelectableChip(
|
|
|
|
|
+ context: context,
|
|
|
|
|
+ cs: cs,
|
|
|
|
|
+ label: _withdrawNetworkTabLabel(net),
|
|
|
|
|
+ selected: state.selectedNetworkId == net.id,
|
|
|
|
|
+ onTap: () {
|
|
|
|
|
+ _amountController.clear();
|
|
|
|
|
+ notifier.selectNetwork(net.id);
|
|
|
|
|
+ setState(() {});
|
|
|
|
|
+ },
|
|
|
),
|
|
),
|
|
|
- ),
|
|
|
|
|
- );
|
|
|
|
|
- }),
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+ ],
|
|
|
|
|
+ ),
|
|
|
|
|
+ if (state.selectedNetworkId > 0) ...[
|
|
|
|
|
+ const SizedBox(height: 16),
|
|
|
|
|
+ _Label(l10n.depositSubCoin),
|
|
|
|
|
+ const SizedBox(height: 8),
|
|
|
|
|
+ if (state.onchainSubCoinsLoading)
|
|
|
|
|
+ const LinearProgressIndicator()
|
|
|
|
|
+ else if (state.onchainSubCoins.isEmpty)
|
|
|
|
|
+ Text(
|
|
|
|
|
+ l10n.depositNoSubCoin,
|
|
|
|
|
+ style: TextStyle(color: cs.onSurface.withAlpha(140), fontSize: 13),
|
|
|
|
|
+ )
|
|
|
|
|
+ else
|
|
|
|
|
+ Wrap(
|
|
|
|
|
+ children: [
|
|
|
|
|
+ for (var i = 0; i < state.onchainSubCoins.length; i++)
|
|
|
|
|
+ _buildSelectableChip(
|
|
|
|
|
+ context: context,
|
|
|
|
|
+ cs: cs,
|
|
|
|
|
+ label: _subCoinTabLabel(state.onchainSubCoins[i]),
|
|
|
|
|
+ selected: state.selectedSubCoinIndex == i,
|
|
|
|
|
+ onTap: () {
|
|
|
|
|
+ _amountController.clear();
|
|
|
|
|
+ notifier.selectSubCoin(i);
|
|
|
|
|
+ setState(() {});
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ ],
|
|
|
|
|
+ ),
|
|
|
|
|
+ ],
|
|
|
const SizedBox(height: 16),
|
|
const SizedBox(height: 16),
|
|
|
|
|
|
|
|
// 提币地址
|
|
// 提币地址
|
|
|
- _Label(AppLocalizations.of(context)!.withdrawAddress),
|
|
|
|
|
|
|
+ _Label(l10n.withdrawAddress),
|
|
|
const SizedBox(height: 8),
|
|
const SizedBox(height: 8),
|
|
|
_InputField(
|
|
_InputField(
|
|
|
controller: _addressController,
|
|
controller: _addressController,
|
|
|
- hint: AppLocalizations.of(context)!.enterWithdrawAddress,
|
|
|
|
|
|
|
+ hint: l10n.withdrawAddressForCoin(coinSymbol),
|
|
|
suffixIcon: Icon(Icons.qr_code_scanner, size: 20, color: cs.onSurface.withAlpha(153)),
|
|
suffixIcon: Icon(Icons.qr_code_scanner, size: 20, color: cs.onSurface.withAlpha(153)),
|
|
|
onSuffixIconTap: _scanQrCode,
|
|
onSuffixIconTap: _scanQrCode,
|
|
|
),
|
|
),
|
|
@@ -318,7 +420,7 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
// 提币金额
|
|
// 提币金额
|
|
|
Row(
|
|
Row(
|
|
|
children: [
|
|
children: [
|
|
|
- _Label(AppLocalizations.of(context)!.withdrawAmount),
|
|
|
|
|
|
|
+ _Label(l10n.withdrawAmountCoin(coinSymbol)),
|
|
|
const SizedBox(width: 4),
|
|
const SizedBox(width: 4),
|
|
|
Icon(Icons.info_outline, size: 14, color: cs.onSurface.withAlpha(120)),
|
|
Icon(Icons.info_outline, size: 14, color: cs.onSurface.withAlpha(120)),
|
|
|
],
|
|
],
|
|
@@ -326,21 +428,21 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
const SizedBox(height: 8),
|
|
const SizedBox(height: 8),
|
|
|
_InputField(
|
|
_InputField(
|
|
|
controller: _amountController,
|
|
controller: _amountController,
|
|
|
- hint: AppLocalizations.of(context)!.withdrawMinAmountHint(minAmount),
|
|
|
|
|
|
|
+ hint: l10n.withdrawMinAmountHint(minAmount),
|
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
|
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,4}'))],
|
|
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,4}'))],
|
|
|
onChanged: (_) => setState(() {}),
|
|
onChanged: (_) => setState(() {}),
|
|
|
suffix: Row(
|
|
suffix: Row(
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
children: [
|
|
children: [
|
|
|
- Text('USDT', style: TextStyle(color: cs.onSurface.withAlpha(153), fontSize: 14)),
|
|
|
|
|
|
|
+ Text(coinSymbol, style: TextStyle(color: cs.onSurface.withAlpha(153), fontSize: 14)),
|
|
|
const SizedBox(width: 8),
|
|
const SizedBox(width: 8),
|
|
|
GestureDetector(
|
|
GestureDetector(
|
|
|
onTap: () {
|
|
onTap: () {
|
|
|
_amountController.text = available.toString();
|
|
_amountController.text = available.toString();
|
|
|
setState(() {});
|
|
setState(() {});
|
|
|
},
|
|
},
|
|
|
- child: Text(AppLocalizations.of(context)!.max, style: const TextStyle(color: AppColors.brand, fontSize: 14, fontWeight: FontWeight.w600)),
|
|
|
|
|
|
|
+ child: Text(l10n.max, style: const TextStyle(color: AppColors.brand, fontSize: 14, fontWeight: FontWeight.w600)),
|
|
|
),
|
|
),
|
|
|
],
|
|
],
|
|
|
),
|
|
),
|
|
@@ -348,26 +450,26 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
const SizedBox(height: 6),
|
|
const SizedBox(height: 6),
|
|
|
Row(
|
|
Row(
|
|
|
children: [
|
|
children: [
|
|
|
- Text('${AppLocalizations.of(context)!.fundAccountAvailable}:', style: TextStyle(color: cs.onSurface.withAlpha(153), fontSize: 12)),
|
|
|
|
|
- Flexible(child: Text('$available USDT ', overflow: TextOverflow.ellipsis, style: TextStyle(color: cs.onSurface, fontSize: 12, fontWeight: FontWeight.w600))),
|
|
|
|
|
|
|
+ Text('${l10n.fundAccountAvailable}:', style: TextStyle(color: cs.onSurface.withAlpha(153), fontSize: 12)),
|
|
|
|
|
+ Flexible(child: Text('$available $coinSymbol ', overflow: TextOverflow.ellipsis, style: TextStyle(color: cs.onSurface, fontSize: 12, fontWeight: FontWeight.w600))),
|
|
|
GestureDetector(
|
|
GestureDetector(
|
|
|
onTap: () async {
|
|
onTap: () async {
|
|
|
await context.push('/asset/transfer');
|
|
await context.push('/asset/transfer');
|
|
|
if (mounted) ref.read(withdrawProvider.notifier).refresh();
|
|
if (mounted) ref.read(withdrawProvider.notifier).refresh();
|
|
|
},
|
|
},
|
|
|
- child: Text(AppLocalizations.of(context)!.transfer, style: const TextStyle(color: AppColors.brand, fontSize: 12, fontWeight: FontWeight.w500)),
|
|
|
|
|
|
|
+ child: Text(l10n.transfer, style: const TextStyle(color: AppColors.brand, fontSize: 12, fontWeight: FontWeight.w500)),
|
|
|
),
|
|
),
|
|
|
],
|
|
],
|
|
|
),
|
|
),
|
|
|
const SizedBox(height: 20),
|
|
const SizedBox(height: 20),
|
|
|
|
|
|
|
|
// 到账数量
|
|
// 到账数量
|
|
|
- _Label(AppLocalizations.of(context)!.receivedAmount),
|
|
|
|
|
|
|
+ _Label(l10n.receivedAmount),
|
|
|
const SizedBox(height: 8),
|
|
const SizedBox(height: 8),
|
|
|
Container(
|
|
Container(
|
|
|
padding: const EdgeInsets.all(12),
|
|
padding: const EdgeInsets.all(12),
|
|
|
decoration: BoxDecoration(color: isDark ? AppColors.darkBgTertiary : AppColors.lightBgTertiary, borderRadius: BorderRadius.circular(8)),
|
|
decoration: BoxDecoration(color: isDark ? AppColors.darkBgTertiary : AppColors.lightBgTertiary, borderRadius: BorderRadius.circular(8)),
|
|
|
- child: Text('$receiveDisplay USDT', style: TextStyle(color: cs.onSurface, fontSize: 16, fontWeight: FontWeight.w600)),
|
|
|
|
|
|
|
+ child: Text('$receiveDisplay $coinSymbol', style: TextStyle(color: cs.onSurface, fontSize: 16, fontWeight: FontWeight.w600)),
|
|
|
),
|
|
),
|
|
|
const SizedBox(height: 16),
|
|
const SizedBox(height: 16),
|
|
|
|
|
|
|
@@ -375,8 +477,8 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
Row(
|
|
Row(
|
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
|
children: [
|
|
children: [
|
|
|
- _Label(AppLocalizations.of(context)!.fee),
|
|
|
|
|
- Text('$fee USDT', style: TextStyle(color: cs.onSurface, fontSize: 14, fontWeight: FontWeight.w600)),
|
|
|
|
|
|
|
+ _Label(l10n.fee),
|
|
|
|
|
+ Text('$fee $coinSymbol', style: TextStyle(color: cs.onSurface, fontSize: 14, fontWeight: FontWeight.w600)),
|
|
|
],
|
|
],
|
|
|
),
|
|
),
|
|
|
const SizedBox(height: 20),
|
|
const SizedBox(height: 20),
|
|
@@ -390,7 +492,9 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
width: double.infinity,
|
|
width: double.infinity,
|
|
|
height: 50,
|
|
height: 50,
|
|
|
child: ElevatedButton(
|
|
child: ElevatedButton(
|
|
|
- onPressed: state.isSubmitting ? null : () => _submitOnChain(notifier),
|
|
|
|
|
|
|
+ onPressed: state.isSubmitting || state.onchainSelectedCoin == null
|
|
|
|
|
+ ? null
|
|
|
|
|
+ : () => _submitOnChain(notifier),
|
|
|
style: ElevatedButton.styleFrom(
|
|
style: ElevatedButton.styleFrom(
|
|
|
backgroundColor: AppColors.brand,
|
|
backgroundColor: AppColors.brand,
|
|
|
disabledBackgroundColor: AppColors.brand.withAlpha(80),
|
|
disabledBackgroundColor: AppColors.brand.withAlpha(80),
|
|
@@ -399,7 +503,7 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
),
|
|
),
|
|
|
child: state.isSubmitting
|
|
child: state.isSubmitting
|
|
|
? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.black))
|
|
? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.black))
|
|
|
- : Text(AppLocalizations.of(context)!.withdrawCoin, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
|
|
|
|
|
|
|
+ : Text(l10n.withdrawCoin, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
|
|
|
),
|
|
),
|
|
|
),
|
|
),
|
|
|
const SizedBox(height: 16),
|
|
const SizedBox(height: 16),
|
|
@@ -418,39 +522,82 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
|
|
|
|
|
Widget _buildInternal(BuildContext context, WithdrawState state, WithdrawNotifier notifier) {
|
|
Widget _buildInternal(BuildContext context, WithdrawState state, WithdrawNotifier notifier) {
|
|
|
final cs = Theme.of(context).colorScheme;
|
|
final cs = Theme.of(context).colorScheme;
|
|
|
|
|
+ final isDark = Theme.of(context).brightness == Brightness.dark;
|
|
|
|
|
+ final l10n = AppLocalizations.of(context)!;
|
|
|
final available = state.transferableBalance;
|
|
final available = state.transferableBalance;
|
|
|
|
|
+ final coinSymbol = state.transferCoinSymbol;
|
|
|
|
|
|
|
|
return Column(
|
|
return Column(
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
children: [
|
|
children: [
|
|
|
- _Label(AppLocalizations.of(context)!.selectCoin),
|
|
|
|
|
- const SizedBox(height: 8),
|
|
|
|
|
- _FixedCoinField(coin: 'USDT'),
|
|
|
|
|
- const SizedBox(height: 16),
|
|
|
|
|
|
|
+ if (state.transferMainCoins.isNotEmpty) ...[
|
|
|
|
|
+ _Label(l10n.withdrawMainCoinLabel),
|
|
|
|
|
+ const SizedBox(height: 8),
|
|
|
|
|
+ Container(
|
|
|
|
|
+ padding: const EdgeInsets.symmetric(horizontal: 12),
|
|
|
|
|
+ decoration: BoxDecoration(
|
|
|
|
|
+ color: isDark ? AppColors.darkBgTertiary : AppColors.lightBgTertiary,
|
|
|
|
|
+ borderRadius: BorderRadius.circular(8),
|
|
|
|
|
+ border: Border.all(color: cs.outline.withAlpha(80)),
|
|
|
|
|
+ ),
|
|
|
|
|
+ child: DropdownButtonHideUnderline(
|
|
|
|
|
+ child: DropdownButton<int>(
|
|
|
|
|
+ isExpanded: true,
|
|
|
|
|
+ value: state.selectedTransferCoinIndex.clamp(
|
|
|
|
|
+ 0,
|
|
|
|
|
+ state.transferMainCoins.length - 1,
|
|
|
|
|
+ ),
|
|
|
|
|
+ items: [
|
|
|
|
|
+ for (var i = 0; i < state.transferMainCoins.length; i++)
|
|
|
|
|
+ DropdownMenuItem(
|
|
|
|
|
+ value: i,
|
|
|
|
|
+ child: Text(
|
|
|
|
|
+ _subCoinTabLabel(state.transferMainCoins[i]),
|
|
|
|
|
+ style: TextStyle(color: cs.onSurface, fontSize: 14),
|
|
|
|
|
+ ),
|
|
|
|
|
+ ),
|
|
|
|
|
+ ],
|
|
|
|
|
+ onChanged: (v) {
|
|
|
|
|
+ if (v != null) {
|
|
|
|
|
+ _amountController.clear();
|
|
|
|
|
+ notifier.selectTransferCoin(v);
|
|
|
|
|
+ setState(() {});
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ ),
|
|
|
|
|
+ ),
|
|
|
|
|
+ const SizedBox(height: 6),
|
|
|
|
|
+ Text(
|
|
|
|
|
+ l10n.withdrawTransferMainCoinHint,
|
|
|
|
|
+ style: TextStyle(color: cs.onSurface.withAlpha(120), fontSize: 12),
|
|
|
|
|
+ ),
|
|
|
|
|
+ const SizedBox(height: 16),
|
|
|
|
|
+ ],
|
|
|
|
|
|
|
|
- _Label(AppLocalizations.of(context)!.recipientUidOrAccount),
|
|
|
|
|
|
|
+ _Label(l10n.recipientUidOrAccount),
|
|
|
const SizedBox(height: 8),
|
|
const SizedBox(height: 8),
|
|
|
_InputField(
|
|
_InputField(
|
|
|
controller: _addressController,
|
|
controller: _addressController,
|
|
|
- hint: AppLocalizations.of(context)!.enterRecipientUid,
|
|
|
|
|
|
|
+ hint: l10n.enterRecipientUid,
|
|
|
),
|
|
),
|
|
|
const SizedBox(height: 16),
|
|
const SizedBox(height: 16),
|
|
|
|
|
|
|
|
- _Label(AppLocalizations.of(context)!.withdrawAmount),
|
|
|
|
|
|
|
+ _Label(l10n.transferAmountCoin(coinSymbol)),
|
|
|
const SizedBox(height: 8),
|
|
const SizedBox(height: 8),
|
|
|
_InputField(
|
|
_InputField(
|
|
|
controller: _amountController,
|
|
controller: _amountController,
|
|
|
- hint: AppLocalizations.of(context)!.transferMinAmountHint(state.transferMinAmount),
|
|
|
|
|
|
|
+ hint: l10n.transferMinAmountHint(state.transferMinAmount),
|
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
|
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,4}'))],
|
|
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,4}'))],
|
|
|
suffix: Row(
|
|
suffix: Row(
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
children: [
|
|
children: [
|
|
|
- Text('USDT', style: TextStyle(color: cs.onSurface.withAlpha(153), fontSize: 14)),
|
|
|
|
|
|
|
+ Text(coinSymbol, style: TextStyle(color: cs.onSurface.withAlpha(153), fontSize: 14)),
|
|
|
const SizedBox(width: 8),
|
|
const SizedBox(width: 8),
|
|
|
GestureDetector(
|
|
GestureDetector(
|
|
|
onTap: () => _amountController.text = available.toString(),
|
|
onTap: () => _amountController.text = available.toString(),
|
|
|
- child: Text(AppLocalizations.of(context)!.max, style: const TextStyle(color: AppColors.brand, fontSize: 14, fontWeight: FontWeight.w600)),
|
|
|
|
|
|
|
+ child: Text(l10n.max, style: const TextStyle(color: AppColors.brand, fontSize: 14, fontWeight: FontWeight.w600)),
|
|
|
),
|
|
),
|
|
|
],
|
|
],
|
|
|
),
|
|
),
|
|
@@ -458,14 +605,14 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
const SizedBox(height: 6),
|
|
const SizedBox(height: 6),
|
|
|
Row(
|
|
Row(
|
|
|
children: [
|
|
children: [
|
|
|
- Text('${AppLocalizations.of(context)!.fundAccountAvailable}:', style: TextStyle(color: cs.onSurface.withAlpha(153), fontSize: 12)),
|
|
|
|
|
- Flexible(child: Text('$available USDT ', overflow: TextOverflow.ellipsis, style: TextStyle(color: cs.onSurface, fontSize: 12, fontWeight: FontWeight.w600))),
|
|
|
|
|
|
|
+ Text('${l10n.fundAccountAvailable}:', style: TextStyle(color: cs.onSurface.withAlpha(153), fontSize: 12)),
|
|
|
|
|
+ Flexible(child: Text('$available $coinSymbol ', overflow: TextOverflow.ellipsis, style: TextStyle(color: cs.onSurface, fontSize: 12, fontWeight: FontWeight.w600))),
|
|
|
GestureDetector(
|
|
GestureDetector(
|
|
|
onTap: () async {
|
|
onTap: () async {
|
|
|
await context.push('/asset/transfer');
|
|
await context.push('/asset/transfer');
|
|
|
if (mounted) ref.read(withdrawProvider.notifier).refresh();
|
|
if (mounted) ref.read(withdrawProvider.notifier).refresh();
|
|
|
},
|
|
},
|
|
|
- child: Text(AppLocalizations.of(context)!.transfer, style: const TextStyle(color: AppColors.brand, fontSize: 12, fontWeight: FontWeight.w500)),
|
|
|
|
|
|
|
+ child: Text(l10n.transfer, style: const TextStyle(color: AppColors.brand, fontSize: 12, fontWeight: FontWeight.w500)),
|
|
|
),
|
|
),
|
|
|
],
|
|
],
|
|
|
),
|
|
),
|
|
@@ -478,7 +625,9 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
width: double.infinity,
|
|
width: double.infinity,
|
|
|
height: 50,
|
|
height: 50,
|
|
|
child: ElevatedButton(
|
|
child: ElevatedButton(
|
|
|
- onPressed: state.isSubmitting ? null : () => _submitTransfer(notifier),
|
|
|
|
|
|
|
+ onPressed: state.isSubmitting || state.transferSelectedCoin == null
|
|
|
|
|
+ ? null
|
|
|
|
|
+ : () => _submitTransfer(notifier),
|
|
|
style: ElevatedButton.styleFrom(
|
|
style: ElevatedButton.styleFrom(
|
|
|
backgroundColor: AppColors.brand,
|
|
backgroundColor: AppColors.brand,
|
|
|
foregroundColor: Colors.black,
|
|
foregroundColor: Colors.black,
|
|
@@ -486,7 +635,7 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
),
|
|
),
|
|
|
child: state.isSubmitting
|
|
child: state.isSubmitting
|
|
|
? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.black))
|
|
? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.black))
|
|
|
- : Text(AppLocalizations.of(context)!.transfer, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
|
|
|
|
|
|
|
+ : Text(l10n.transfer, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
|
|
|
),
|
|
),
|
|
|
),
|
|
),
|
|
|
const SizedBox(height: 16),
|
|
const SizedBox(height: 16),
|
|
@@ -678,30 +827,6 @@ class _WithdrawScreenState extends ConsumerState<WithdrawScreen>
|
|
|
|
|
|
|
|
// ── 共享组件 ─────────────────────────────────────────────────
|
|
// ── 共享组件 ─────────────────────────────────────────────────
|
|
|
|
|
|
|
|
-class _FixedCoinField extends StatelessWidget {
|
|
|
|
|
- const _FixedCoinField({required this.coin});
|
|
|
|
|
- final String coin;
|
|
|
|
|
-
|
|
|
|
|
- @override
|
|
|
|
|
- Widget build(BuildContext context) {
|
|
|
|
|
- final cs = Theme.of(context).colorScheme;
|
|
|
|
|
- return Container(
|
|
|
|
|
- padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
|
|
|
|
|
- decoration: BoxDecoration(
|
|
|
|
|
- border: Border.all(color: cs.outline.withAlpha(60)),
|
|
|
|
|
- borderRadius: BorderRadius.circular(10),
|
|
|
|
|
- ),
|
|
|
|
|
- child: Row(
|
|
|
|
|
- children: [
|
|
|
|
|
- Text(coin, style: TextStyle(color: cs.onSurface, fontSize: 15, fontWeight: FontWeight.w500)),
|
|
|
|
|
- const Spacer(),
|
|
|
|
|
- Icon(Icons.keyboard_arrow_down, color: cs.onSurface.withAlpha(153)),
|
|
|
|
|
- ],
|
|
|
|
|
- ),
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
class _Label extends StatelessWidget {
|
|
class _Label extends StatelessWidget {
|
|
|
const _Label(this.text);
|
|
const _Label(this.text);
|
|
|
final String text;
|
|
final String text;
|