mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
fix bridge ui (#3189)
* fix bridge ui * padding on info bottom sheet * fix cutoff on infobottomsheet * fix wording on bridge details ui * no _amountController.clear() on bridge page navigation * autofocus keyboard on bridge page opened * remove KeyboardHideOverlay * fix text field resize * fix text field padding
This commit is contained in:
@@ -4,7 +4,6 @@ import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
|
||||
import 'package:cake_wallet/utils/request_review_handler.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/keyboard_hide_overlay.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/view_model/bridge_history_view_model.dart';
|
||||
@@ -33,7 +32,10 @@ class BridgeAmountPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _BridgeAmountPageState extends State<BridgeAmountPage> {
|
||||
late final TextEditingController _amountController;
|
||||
final TextEditingController _amountController = TextEditingController();
|
||||
final FocusNode _amountFocusNode = FocusNode();
|
||||
bool _amountFocused = false;
|
||||
|
||||
BridgeViewModel get bridgeViewModel => widget.bridgeViewModel;
|
||||
|
||||
@override
|
||||
@@ -42,10 +44,11 @@ class _BridgeAmountPageState extends State<BridgeAmountPage> {
|
||||
bridgeViewModel.applyInitialBridgeToken(widget.initialToken);
|
||||
bridgeViewModel.onBridgeSuccess = _showBridgeSuccessBottomSheet;
|
||||
|
||||
_amountController = TextEditingController();
|
||||
_amountFocusNode.addListener(() => setState(() => _amountFocused = _amountFocusNode.hasFocus));
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
bridgeViewModel.ensureFiatPriceForSelectedToken();
|
||||
_amountFocusNode.requestFocus();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -100,22 +103,23 @@ class _BridgeAmountPageState extends State<BridgeAmountPage> {
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return KeyboardHideOverlay(
|
||||
unfocusOnTap: true,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surface,
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
|
||||
),
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surface,
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
|
||||
child: Column(
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: 'Enter Amount',
|
||||
leadingIcon: const Icon(Icons.arrow_back_ios_new, size: 18),
|
||||
title: S.of(context).enter_amount,
|
||||
leadingIcon: Icon(Icons.arrow_back_ios_new),
|
||||
onLeadingPressed: () => Navigator.of(context, rootNavigator: true).pop(),
|
||||
trailingIcon: const Icon(Icons.calendar_month, size: 18),
|
||||
trailingIcon: Icon(Icons.history),
|
||||
onTrailingPressed: () {
|
||||
Navigator.pushNamed(context, Routes.bridgeHistoryPage, arguments: widget.bridgeHistoryViewModel);
|
||||
Navigator.pushNamed(context, Routes.bridgeHistoryPage,
|
||||
arguments: widget.bridgeHistoryViewModel);
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
@@ -130,66 +134,63 @@ class _BridgeAmountPageState extends State<BridgeAmountPage> {
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
const Spacer(flex: 2),
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return Center(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: (constraints.maxWidth - 200)
|
||||
.clamp(40.0, constraints.maxWidth),
|
||||
minWidth: 40,
|
||||
),
|
||||
child: TextFormField(
|
||||
controller: _amountController,
|
||||
maxLines: 1,
|
||||
onChanged: bridgeViewModel.setAmount,
|
||||
autovalidateMode: AutovalidateMode.always,
|
||||
validator: bridgeViewModel.decimalAmountValidator,
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
decimal: true,
|
||||
),
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter.allow(
|
||||
RegExp(r'^\d*[.,]?\d*$'),
|
||||
),
|
||||
],
|
||||
textAlign: TextAlign.center,
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
hintText: '0.00',
|
||||
hintStyle: theme.textTheme.displayMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
style: theme.textTheme.displayMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 45,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
IntrinsicWidth(
|
||||
child: TextFormField(
|
||||
controller: _amountController,
|
||||
focusNode: _amountFocusNode,
|
||||
maxLines: 1,
|
||||
onChanged: bridgeViewModel.setAmount,
|
||||
autovalidateMode: AutovalidateMode.always,
|
||||
validator: bridgeViewModel.decimalAmountValidator,
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false,
|
||||
decimal: true,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
bridgeViewModel.selectedToken?.title ?? '',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.displayMedium?.copyWith(
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter.allow(
|
||||
RegExp(r'^\d*[.,]?\d*$'),
|
||||
),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
isDense: true,
|
||||
isCollapsed: true,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
fillColor: Colors.transparent,
|
||||
hoverColor: Colors.transparent,
|
||||
focusedBorder: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
hintText: _amountFocused || _amountController.text.isNotEmpty ? null : "0.00",
|
||||
hintStyle: theme.textTheme.displayMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 45,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
style: theme.textTheme.displayMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 45,
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
bridgeViewModel.selectedToken?.title ?? '',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.displayMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 45,
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Center(
|
||||
@@ -217,78 +218,71 @@ class _BridgeAmountPageState extends State<BridgeAmountPage> {
|
||||
],
|
||||
const Spacer(flex: 3),
|
||||
Row(
|
||||
spacing: 8,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: RichText(
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
text: TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: 'Available ',
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
letterSpacing: -0.08,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: '${bridgeViewModel.tokenBalanceFormatted} '
|
||||
'${bridgeViewModel.selectedToken?.title ?? ''}',
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
letterSpacing: -0.08,
|
||||
),
|
||||
),
|
||||
],
|
||||
child: Row(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).available,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle: theme.textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
letterSpacing: -0.07,
|
||||
),
|
||||
foregroundColor: theme.colorScheme.primary,
|
||||
backgroundColor: theme.colorScheme.surfaceContainer,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(80),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Text(
|
||||
"${bridgeViewModel.tokenBalanceFormatted} ${bridgeViewModel.selectedToken?.title ?? ""}",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).colorScheme.onSurface),
|
||||
)
|
||||
],
|
||||
)),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
bridgeViewModel.setMaxAmount();
|
||||
_amountController.text = bridgeViewModel.amount;
|
||||
_amountController.selection = TextSelection.collapsed(
|
||||
offset: _amountController.text.length,
|
||||
);
|
||||
},
|
||||
child: Text(S.of(context).max),
|
||||
child: Container(
|
||||
height: 34,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(999999)),
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
S.of(context).max,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).colorScheme.primary),
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
IgnorePointer(
|
||||
ignoring: !canNext,
|
||||
child: ModernButton(
|
||||
size: 48,
|
||||
size: 34,
|
||||
backgroundColor: theme.colorScheme.primary,
|
||||
iconColor: theme.colorScheme.onPrimary,
|
||||
icon: Icon(Icons.arrow_forward, size: 25),
|
||||
icon: Icon(Icons.arrow_forward, size: 20),
|
||||
onPressed: () {
|
||||
_amountController.clear();
|
||||
Navigator.pushNamed(
|
||||
context, Routes.bridgeDestinationNetworkPage, arguments: bridgeViewModel);
|
||||
context, Routes.bridgeDestinationNetworkPage,
|
||||
arguments: bridgeViewModel);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 24,
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
@@ -302,4 +296,3 @@ class _BridgeAmountPageState extends State<BridgeAmountPage> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/send_page/send_confirm_bottom_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cake_wallet/view_model/bridge/bridge_view_model.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
@@ -98,7 +99,7 @@ class _BridgeConfirmSheetState extends State<BridgeConfirmSheet> {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 48),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
child: CupertinoActivityIndicator(),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -176,9 +177,14 @@ class _BridgeConfirmSheetState extends State<BridgeConfirmSheet> {
|
||||
),
|
||||
],
|
||||
),
|
||||
NetworkPathPill(
|
||||
sourceChainName: bridgeViewModel.wallet.type.name,
|
||||
destChainName: bridgeViewModel.destinationChainInfo?.name ?? '',
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
NetworkPathPill(
|
||||
sourceChainName: bridgeViewModel.wallet.type.name,
|
||||
destChainName: bridgeViewModel.destinationChainInfo?.name ?? '',
|
||||
),
|
||||
],
|
||||
),
|
||||
ConfirmDetailsCard(bridgeViewModel: bridgeViewModel),
|
||||
if (bridgeViewModel.executeError != null) ...[
|
||||
@@ -199,22 +205,16 @@ class _BridgeConfirmSheetState extends State<BridgeConfirmSheet> {
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
if (bridgeViewModel.isExecuting) {
|
||||
return const LoadingBottomWidget(
|
||||
text: 'Bridging...',
|
||||
);
|
||||
return LoadingBottomWidget(text: "${S.of(context).bridging}...",);
|
||||
}
|
||||
final canSwipe = bridgeViewModel.quote != null && !bridgeViewModel.isQuoteLoading;
|
||||
return IgnorePointer(
|
||||
ignoring: !canSwipe,
|
||||
child: Opacity(
|
||||
opacity: canSwipe ? 1 : 0.45,
|
||||
child: ConfirmSwiper(
|
||||
onConfirmed: () {
|
||||
bridgeViewModel.executeBridge();
|
||||
},
|
||||
swiperText: 'Swipe to bridge',
|
||||
),
|
||||
),
|
||||
if(bridgeViewModel.isQuoteLoading || bridgeViewModel.quote == null) {
|
||||
return LoadingBottomWidget(text: "${S.of(context).loading}...");
|
||||
}
|
||||
return ConfirmSwiper(
|
||||
onConfirmed: () {
|
||||
bridgeViewModel.executeBridge();
|
||||
},
|
||||
swiperText: S.of(context).swipe_to_bridge,
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
@@ -28,18 +28,16 @@ class NetworkPathPill extends StatelessWidget {
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: Text(
|
||||
sourceChainName.capitalized(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: scheme.onSurface,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
letterSpacing: -0.08,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
sourceChainName.capitalized(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: scheme.onSurface,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
letterSpacing: -0.08,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
@@ -56,18 +54,16 @@ class NetworkPathPill extends StatelessWidget {
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: Text(
|
||||
destChainName,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: scheme.onSurface,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
letterSpacing: -0.08,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
destChainName,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
||||
color: scheme.onSurface,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
letterSpacing: -0.08,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -55,13 +55,15 @@ abstract class BaseBottomSheet extends StatelessWidget {
|
||||
topLeft: Radius.circular(30.0), topRight: Radius.circular(30.0)),
|
||||
child: Container(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
_buildHeader(context),
|
||||
contentWidget(context),
|
||||
_buildFooter(context),
|
||||
],
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
_buildHeader(context),
|
||||
contentWidget(context),
|
||||
_buildFooter(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -108,7 +110,7 @@ abstract class BaseBottomSheet extends StatelessWidget {
|
||||
|
||||
case FooterType.slideActionButton:
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(40, 12, 40, 34),
|
||||
padding: const EdgeInsets.fromLTRB(40, 12, 40, 24),
|
||||
child: StandardSlideButton(
|
||||
key: ValueKey('base_bottom_sheet_widget_standard_slide_button_key'),
|
||||
buttonText: slideActionButtonText ?? '',
|
||||
@@ -120,7 +122,7 @@ abstract class BaseBottomSheet extends StatelessWidget {
|
||||
|
||||
case FooterType.singleActionButton:
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 34),
|
||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 24),
|
||||
child: LoadingPrimaryButton(
|
||||
key: singleActionButtonKey,
|
||||
text: singleActionButtonText ?? '',
|
||||
@@ -134,7 +136,7 @@ abstract class BaseBottomSheet extends StatelessWidget {
|
||||
|
||||
case FooterType.doubleActionButton:
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 34),
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 24),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
|
||||
@@ -100,6 +100,7 @@ class InfoBottomSheet extends BaseBottomSheet {
|
||||
)
|
||||
else
|
||||
Container(),
|
||||
SizedBox(height: 24,),
|
||||
if (content != null)
|
||||
Expanded(
|
||||
flex: 2,
|
||||
|
||||
@@ -17,6 +17,7 @@ import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/bridge_transfers_store.dart';
|
||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cw_core/crypto_amount_format.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/erc20_token.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
@@ -187,7 +188,7 @@ abstract class BridgeViewModelBase extends WalletChangeListenerViewModel with St
|
||||
String get quoteNativeFeeFormattedForDisplay {
|
||||
if (quoteNativeFee.isEmpty) return '';
|
||||
|
||||
return '${quoteNativeFee} ${wallet.currency.title}';
|
||||
return '${quoteNativeFee.withMaxDecimals(8)} ${wallet.currency.title}';
|
||||
}
|
||||
|
||||
@computed
|
||||
|
||||
@@ -97,9 +97,9 @@ abstract class BridgeDetailsViewModelBase with Store {
|
||||
);
|
||||
|
||||
final sourceName =
|
||||
evm?.getChainNameByChainId(transfer.sourceChainId) ?? '${transfer.sourceChainId}';
|
||||
evm?.getChainInfoByChainId(transfer.sourceChainId)?.name ?? '${transfer.sourceChainId}';
|
||||
final destName =
|
||||
evm?.getChainNameByChainId(transfer.destinationChainId) ?? '${transfer.destinationChainId}';
|
||||
evm?.getChainInfoByChainId(transfer.destinationChainId)?.name ?? '${transfer.destinationChainId}';
|
||||
|
||||
items.add(
|
||||
StandartListItem(
|
||||
|
||||
Reference in New Issue
Block a user