mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-26 06:08:40 +01:00
feat Add gradient-only card design and icon style picker (#3054)
* feat Add gradient-only card design and icon style picker * refactor: Migrate card icon style to index-based storage * feat: Add new card icons/styles for icon panel * feat: add generic cake card icon * Update coin icons and add opacity * Remove vector icon files * Remove vector icon files * add xmr-og.webp * feat: add animated switcher to icon style panel * Enhance icson style preview fix special gradient bug on plain style card * Fix card styles ignoring special gradient text color combination * Improve icon mgmt for card design handling, other fixes also based on review too * fix: Add bounds check to icon style index * fix: monero classic icon not displaying --------- Co-authored-by: tuxsudo <tuxsudo@tux.pizza>
This commit is contained in:
@@ -339,10 +339,10 @@ class _AccountCustomizerState extends State<AccountCustomizer> {
|
||||
printV("$i: $idx");
|
||||
|
||||
await BalanceCardStyleSettings.fromCardDesign(
|
||||
widget.dashboardViewModel.wallet.walletInfo.internalId,
|
||||
i,
|
||||
idx,
|
||||
_items[idx].card.design)
|
||||
walletInfoId: widget.dashboardViewModel.wallet.walletInfo.internalId,
|
||||
accountIndex: i,
|
||||
cardOrder: idx,
|
||||
design: _items[idx].card.design)
|
||||
.insert();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/viewmodels/card_customizer/card_customizer_bloc.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/coins_page/cards/balance_card.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cw_core/card_design.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
@@ -23,7 +25,6 @@ class _CardCustomizerState extends State<CardCustomizer> {
|
||||
bool editEnabled = false;
|
||||
late final CardCustomizerBloc bloc;
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -46,192 +47,306 @@ class _CardCustomizerState extends State<CardCustomizer> {
|
||||
}
|
||||
}
|
||||
|
||||
bool _showIconStylePanel(CardCustomizerState state) {
|
||||
final design = state.availableDesigns[state.selectedDesignIndex];
|
||||
return design.backgroundType == CardDesignBackgroundTypes.svgIcon &&
|
||||
state.availableIconPaths.isNotEmpty;
|
||||
}
|
||||
|
||||
CardDesign _cardStylePreviewDesign(CardCustomizerState state, int index) {
|
||||
var design = state.availableDesigns[index].withGradient(state.selectedColor);
|
||||
if (design.backgroundType == CardDesignBackgroundTypes.svgIcon &&
|
||||
state.availableIconPaths.isNotEmpty) {
|
||||
design = design.withIcon(state.availableIconPaths[state.selectedIconIndex]);
|
||||
}
|
||||
return design;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocListener<CardCustomizerBloc, CardCustomizerState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.accountName != current.accountName,
|
||||
listenWhen: (previous, current) => previous.accountName != current.accountName,
|
||||
listener: (context, state) {
|
||||
if(accountNameController.text != state.accountName) {
|
||||
if (accountNameController.text != state.accountName) {
|
||||
accountNameController.text = state.accountName;
|
||||
}
|
||||
},
|
||||
child: BlocBuilder<CardCustomizerBloc, CardCustomizerState>(
|
||||
builder: (context, state) {
|
||||
if (state is CardCustomizerNotLoaded) return SizedBox.shrink();
|
||||
return PopScope(
|
||||
child: SingleChildScrollView(
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
spacing: 25.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: editEnabled ? S.of(context).edit_account : S.of(context).edit_card,
|
||||
leadingIcon: Icon(Icons.close),
|
||||
// trailingIcon: editEnabled ? Icon(Icons.delete_forever) : null,
|
||||
onLeadingPressed: () => Navigator.of(context).maybePop(),
|
||||
// onTrailingPressed: () {},
|
||||
),
|
||||
if (editEnabled)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.of(context).account_name),
|
||||
TextField(
|
||||
maxLength: 32,
|
||||
decoration: InputDecoration(counterText: ""),
|
||||
onChanged: (value) {
|
||||
context.read<CardCustomizerBloc>().add(AccountNameChanged(value));
|
||||
},
|
||||
controller: accountNameController,
|
||||
)
|
||||
],
|
||||
),
|
||||
child: BlocBuilder<CardCustomizerBloc, CardCustomizerState>(
|
||||
builder: (context, state) {
|
||||
if (state is CardCustomizerNotLoaded) return SizedBox.shrink();
|
||||
return PopScope(
|
||||
child: SingleChildScrollView(
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
spacing: 25.0,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ModalTopBar(
|
||||
title: editEnabled ? S.of(context).edit_account : S.of(context).edit_card,
|
||||
leadingIcon: Icon(Icons.close),
|
||||
// trailingIcon: editEnabled ? Icon(Icons.delete_forever) : null,
|
||||
onLeadingPressed: () => Navigator.of(context).maybePop(),
|
||||
// onTrailingPressed: () {},
|
||||
),
|
||||
BalanceCard(
|
||||
width: min(MediaQuery.of(context).size.width * 0.87, 768),
|
||||
selected: true,
|
||||
designSwitchDuration: Duration(milliseconds: 300),
|
||||
accountName:
|
||||
editEnabled ? state.accountName:"" ,
|
||||
balance: "0.00",
|
||||
assetName: state.displaySats ? "sats" : widget.cryptoName,
|
||||
capitalizeAssetName: !state.displaySats,
|
||||
design: state.selectedDesign,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
if (editEnabled)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.of(context).card_style),
|
||||
Container(
|
||||
height: 63,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: state.availableDesigns.length,
|
||||
separatorBuilder: (context, index) {
|
||||
return SizedBox(width: 8.0);
|
||||
},
|
||||
itemBuilder: (context, index) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
context
|
||||
.read<CardCustomizerBloc>()
|
||||
.add(CardDesignSelected(index));
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 300),
|
||||
decoration: ShapeDecoration(
|
||||
shape: RoundedSuperellipseBorder(
|
||||
side: BorderSide(color: ((index == state.selectedDesignIndex )? Theme.of(context).colorScheme.onSurface : Colors.transparent), width: 1),
|
||||
borderRadius: BorderRadiusGeometry.circular(12),
|
||||
),
|
||||
),
|
||||
child: AnimatedScale(
|
||||
duration: Duration(milliseconds: 200),
|
||||
scale: index == state.selectedDesignIndex ? 0.94 : 1,
|
||||
child: BalanceCard(
|
||||
width: 96,
|
||||
borderRadius: 10,
|
||||
selected: false,
|
||||
designSwitchDuration: Duration(milliseconds: 300),
|
||||
design: state.availableDesigns[index],
|
||||
gradient: state.selectedDesign.gradient,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
)),
|
||||
SizedBox(height: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(16)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.of(context).color),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
child: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
spacing: 4, // space between items in a row
|
||||
runSpacing: 8,
|
||||
children:
|
||||
List.generate(state.availableColors.length, (index) {
|
||||
return Material(
|
||||
borderRadius: BorderRadius.circular(999999999),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(999999999),
|
||||
onTap: () {
|
||||
context
|
||||
.read<CardCustomizerBloc>()
|
||||
.add(ColorSelected(index));
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
AnimatedOpacity(
|
||||
duration: Duration(milliseconds: 200),
|
||||
opacity: index == state.selectedColorIndex ? 1 : 0,
|
||||
child: Container(
|
||||
width:32,height:32,decoration: BoxDecoration(borderRadius: BorderRadius.circular(99999999),border: Border.all(color:Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface))
|
||||
),
|
||||
),
|
||||
AnimatedScale(
|
||||
duration: Duration(milliseconds: 200),
|
||||
scale: index == state.selectedColorIndex ? 0.8 : 1,
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(99999999),
|
||||
gradient: state.availableColors[index]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Text(S.of(context).account_name),
|
||||
TextField(
|
||||
maxLength: 32,
|
||||
decoration: InputDecoration(counterText: ""),
|
||||
onChanged: (value) {
|
||||
context.read<CardCustomizerBloc>().add(AccountNameChanged(value));
|
||||
},
|
||||
controller: accountNameController,
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
BalanceCard(
|
||||
width: min(MediaQuery.of(context).size.width * 0.87, 768),
|
||||
selected: true,
|
||||
designSwitchDuration: Duration(milliseconds: 300),
|
||||
accountName: editEnabled ? state.accountName : "",
|
||||
balance: "0.00",
|
||||
assetName: state.displaySats ? "sats" : widget.cryptoName,
|
||||
capitalizeAssetName: !state.displaySats,
|
||||
design: state.selectedDesign,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainer,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.of(context).card_style),
|
||||
Container(
|
||||
height: 63,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: state.availableDesigns.length,
|
||||
separatorBuilder: (context, index) {
|
||||
return SizedBox(width: 8.0);
|
||||
},
|
||||
itemBuilder: (context, index) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
context
|
||||
.read<CardCustomizerBloc>()
|
||||
.add(CardDesignSelected(index));
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 300),
|
||||
decoration: ShapeDecoration(
|
||||
shape: RoundedSuperellipseBorder(
|
||||
side: BorderSide(
|
||||
color:
|
||||
((index == state.selectedDesignIndex)
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
: Colors.transparent),
|
||||
width: 1),
|
||||
borderRadius:
|
||||
BorderRadiusGeometry.circular(12),
|
||||
),
|
||||
),
|
||||
child: AnimatedScale(
|
||||
duration: Duration(milliseconds: 200),
|
||||
scale: index == state.selectedDesignIndex
|
||||
? 0.94
|
||||
: 1,
|
||||
child: BalanceCard(
|
||||
width: 96,
|
||||
borderRadius: 10,
|
||||
selected: false,
|
||||
designSwitchDuration:
|
||||
Duration(milliseconds: 300),
|
||||
design: _cardStylePreviewDesign(state, index),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
)),
|
||||
AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 350),
|
||||
switchInCurve: Curves.easeOut,
|
||||
switchOutCurve: Curves.easeIn,
|
||||
transitionBuilder: (Widget child, Animation<double> animation) {
|
||||
return FadeTransition(
|
||||
opacity: animation,
|
||||
child: SizeTransition(
|
||||
sizeFactor: animation,
|
||||
axisAlignment: -1,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: _showIconStylePanel(state)
|
||||
? Column(
|
||||
key: const ValueKey('icon_style_panel'),
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.of(context).icon_style),
|
||||
SizedBox(
|
||||
height: 48,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: state.availableIconPaths.length,
|
||||
separatorBuilder: (context, _) =>
|
||||
const SizedBox(width: 8.0),
|
||||
itemBuilder: (context, index) {
|
||||
final icon = state.availableIconPaths[index];
|
||||
final isSelected =
|
||||
index == state.selectedIconIndex;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
context
|
||||
.read<CardCustomizerBloc>()
|
||||
.add(IconStyleSelected(index));
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
duration:
|
||||
const Duration(milliseconds: 200),
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceContainerHigh,
|
||||
border: Border.all(
|
||||
color: isSelected
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface
|
||||
: Colors.transparent,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: CakeImageWidget(imageUrl: icon.path),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: const SizedBox.shrink(key: ValueKey('icon_style_hidden')),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(16)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
spacing: 8.0,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(S.of(context).color),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
child: Wrap(
|
||||
direction: Axis.horizontal,
|
||||
spacing: 4, // space between items in a row
|
||||
runSpacing: 8,
|
||||
children: List.generate(state.availableColors.length,
|
||||
(index) {
|
||||
return Material(
|
||||
borderRadius: BorderRadius.circular(999999999),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(999999999),
|
||||
onTap: () {
|
||||
context
|
||||
.read<CardCustomizerBloc>()
|
||||
.add(ColorSelected(index));
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
AnimatedOpacity(
|
||||
duration: Duration(milliseconds: 200),
|
||||
opacity: index == state.selectedColorIndex
|
||||
? 1
|
||||
: 0,
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(99999999),
|
||||
border: Border.all(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onSurface))),
|
||||
),
|
||||
AnimatedScale(
|
||||
duration: Duration(milliseconds: 200),
|
||||
scale: index == state.selectedColorIndex
|
||||
? 0.8
|
||||
: 1,
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.circular(99999999),
|
||||
gradient:
|
||||
state.availableColors[index]),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,14 @@ class CardCustomizerBloc extends Bloc<CardCustomizerEvent, CardCustomizerState>
|
||||
final bool displaySats;
|
||||
|
||||
CardCustomizerBloc(this._wallet, {this.lightningMode = false, this.displaySats = false})
|
||||
: super(CardCustomizerNotLoaded(0, 0, [CardDesign.genericDefault], [], "", -1, displaySats, 0)) {
|
||||
: super(CardCustomizerNotLoaded(
|
||||
0, 0, [CardDesign.genericDefault], [],
|
||||
"", -1, displaySats, 0)) {
|
||||
|
||||
on<_Init>(_init);
|
||||
on<CardDesignSelected>(_onDesignSelected);
|
||||
on<ColorSelected>(_onColorSelected);
|
||||
on<IconStyleSelected>(_onIconStyleSelected);
|
||||
on<DesignSaved>(_onDesignSaved);
|
||||
on<AccountNameChanged>(_onAccountNameChanged);
|
||||
|
||||
@@ -46,6 +49,7 @@ class CardCustomizerBloc extends Bloc<CardCustomizerEvent, CardCustomizerState>
|
||||
final List<CardDesign> ret = List<CardDesign>.empty(growable: true);
|
||||
final curr = lightningMode ? CryptoCurrency.btcln : _wallet.currency;
|
||||
|
||||
ret.add(CardDesign.gradientOnlyDesign);
|
||||
ret.add(CardDesign.forCurrencyIcon(curr));
|
||||
|
||||
if (CardDesign.specialDesignsForCurrencies[curr] != null)
|
||||
@@ -55,26 +59,30 @@ class CardCustomizerBloc extends Bloc<CardCustomizerEvent, CardCustomizerState>
|
||||
}
|
||||
|
||||
int _initSelectedDesign(CardDesign currentDesign) {
|
||||
if (currentDesign.backgroundType == CardDesignBackgroundTypes.svgIcon)
|
||||
if (currentDesign.backgroundType == CardDesignBackgroundTypes.gradientOnly) {
|
||||
return 0;
|
||||
else if (currentDesign.backgroundType == CardDesignBackgroundTypes.svgFull)
|
||||
}
|
||||
if (currentDesign.backgroundType == CardDesignBackgroundTypes.svgIcon) {
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
if (currentDesign.backgroundType == CardDesignBackgroundTypes.svgFull) {
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _initSelectedIconIndex(
|
||||
BalanceCardStyleSettings? settings,
|
||||
List<CardIconPath> availableIconPaths,
|
||||
) {
|
||||
if (settings == null || availableIconPaths.isEmpty) return 0;
|
||||
if (settings.iconStyleIndex >= availableIconPaths.length) return 0;
|
||||
return settings.iconStyleIndex;
|
||||
}
|
||||
|
||||
int _initSelectedColor(CardDesign currentDesign) {
|
||||
int ret = CardDesign.allGradients.indexOf(currentDesign.gradient);
|
||||
if(ret == -1) {
|
||||
// special design with its own color. select last color in list.
|
||||
return CardDesign.allGradients.length;
|
||||
} else if (ret == -1) {
|
||||
// no color selected, select default.
|
||||
return 0;
|
||||
} else {
|
||||
// select whatever's selected.
|
||||
return ret;
|
||||
}
|
||||
final ret = CardDesign.allGradients.indexOf(currentDesign.gradient);
|
||||
return ret == -1 ? CardDesign.allGradients.length : ret;
|
||||
}
|
||||
|
||||
void _init(_Init event, Emitter<CardCustomizerState> emit) async {
|
||||
@@ -95,24 +103,40 @@ class CardCustomizerBloc extends Bloc<CardCustomizerEvent, CardCustomizerState>
|
||||
} else {
|
||||
accountIndex = -1;
|
||||
}
|
||||
final curr =
|
||||
lightningMode ? CryptoCurrency.btcln : _wallet.currency;
|
||||
final currentDesignSettings = await _loadCurrentDesignSettings(accountIndex);
|
||||
final currentDesign = CardDesign.fromStyleSettings(currentDesignSettings, lightningMode ? CryptoCurrency.btcln : _wallet.currency);
|
||||
final currentDesign = CardDesign.fromStyleSettings(currentDesignSettings, curr);
|
||||
final availableDesigns = _initAvailableDesigns(lightningMode: lightningMode);
|
||||
final availableColors = _updateAvailableColors(currentDesign);
|
||||
final selectedDesign = _initSelectedDesign(currentDesign);
|
||||
final selectedDesignIndex = _initSelectedDesign(currentDesign);
|
||||
final selectedColor = _initSelectedColor(currentDesign);
|
||||
final availableIconPaths = CardDesign.iconPathsForWalletType(curr);
|
||||
final selectedIconIndex = _initSelectedIconIndex(
|
||||
currentDesignSettings, availableIconPaths);
|
||||
|
||||
emit(CardCustomizerInitial(selectedDesign, selectedColor, availableDesigns, availableColors,
|
||||
accountName, accountIndex, displaySats, currentDesignSettings?.cardOrder ?? 0));
|
||||
emit(CardCustomizerInitial(
|
||||
selectedDesignIndex,
|
||||
selectedColor,
|
||||
availableDesigns,
|
||||
availableColors,
|
||||
accountName,
|
||||
accountIndex,
|
||||
displaySats,
|
||||
currentDesignSettings?.cardOrder ?? 0,
|
||||
availableIconPaths: availableIconPaths,
|
||||
selectedIconIndex: selectedIconIndex));
|
||||
}
|
||||
|
||||
void _onDesignSelected(CardDesignSelected event, Emitter<CardCustomizerState> emit) {
|
||||
final newColors = _updateAvailableColors(state.availableDesigns[event.newDesignIndex]);
|
||||
late final int newColorIndex;
|
||||
if (newColors.length < state.availableColors.length) {
|
||||
if (newColors.isEmpty) {
|
||||
newColorIndex = 0;
|
||||
} else if (newColors.length < state.availableColors.length) {
|
||||
newColorIndex = 0;
|
||||
} else {
|
||||
newColorIndex = state.selectedColorIndex;
|
||||
newColorIndex = state.selectedColorIndex.clamp(0, newColors.length - 1);
|
||||
}
|
||||
|
||||
emit(state.copyWith(
|
||||
@@ -125,17 +149,36 @@ class CardCustomizerBloc extends Bloc<CardCustomizerEvent, CardCustomizerState>
|
||||
emit(state.copyWith(selectedColorIndex: event.newColorIndex));
|
||||
}
|
||||
|
||||
void _onIconStyleSelected(
|
||||
IconStyleSelected event, Emitter<CardCustomizerState> emit) {
|
||||
emit(state.copyWith(selectedIconIndex: event.iconIndex));
|
||||
}
|
||||
|
||||
void _onAccountNameChanged(AccountNameChanged event, Emitter<CardCustomizerState> emit) {
|
||||
emit(state.copyWith(accountName: event.newAccountName));
|
||||
}
|
||||
|
||||
void _onDesignSaved(DesignSaved event, Emitter<CardCustomizerState> emit) {
|
||||
BalanceCardStyleSettings.fromCardDesign(
|
||||
_wallet.walletInfo.internalId, state.accountIndex, state.cardOrder, state.selectedDesign)
|
||||
walletInfoId: _wallet.walletInfo.internalId,
|
||||
accountIndex: state.accountIndex,
|
||||
cardOrder: state.cardOrder,
|
||||
design: state.selectedDesign,
|
||||
iconStyleIndex: state.selectedIconIndex,
|
||||
gradientIndexOverride: state.selectedColorIndex)
|
||||
.insert()
|
||||
.then((value) {
|
||||
emit(CardCustomizerSaved(state.selectedDesignIndex, state.selectedColorIndex,
|
||||
state.availableDesigns, state.availableColors, state.accountName, state.accountIndex, state.displaySats, state.cardOrder));
|
||||
emit(CardCustomizerSaved(
|
||||
state.selectedDesignIndex,
|
||||
state.selectedColorIndex,
|
||||
state.availableDesigns,
|
||||
state.availableColors,
|
||||
state.accountName,
|
||||
state.accountIndex,
|
||||
state.displaySats,
|
||||
state.cardOrder,
|
||||
availableIconPaths: state.availableIconPaths,
|
||||
selectedIconIndex: state.selectedIconIndex));
|
||||
});
|
||||
saveAccountName();
|
||||
}
|
||||
|
||||
@@ -26,3 +26,9 @@ class AccountNameChanged extends CardCustomizerEvent {
|
||||
|
||||
|
||||
class DesignSaved extends CardCustomizerEvent {}
|
||||
|
||||
class IconStyleSelected extends CardCustomizerEvent {
|
||||
final int iconIndex;
|
||||
|
||||
IconStyleSelected(this.iconIndex);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ sealed class CardCustomizerState {
|
||||
final bool displaySats;
|
||||
final List<CardDesign> availableDesigns;
|
||||
final List<Gradient> availableColors;
|
||||
final List<CardIconPath> availableIconPaths;
|
||||
final int selectedIconIndex;
|
||||
|
||||
CardCustomizerState(
|
||||
this.selectedDesignIndex,
|
||||
@@ -17,12 +19,30 @@ sealed class CardCustomizerState {
|
||||
this.availableColors,
|
||||
this.accountName,
|
||||
this.accountIndex,
|
||||
this.displaySats,
|
||||
this.cardOrder
|
||||
);
|
||||
this.displaySats,
|
||||
this.cardOrder, {
|
||||
this.availableIconPaths = const [],
|
||||
this.selectedIconIndex = 0,
|
||||
});
|
||||
|
||||
CardDesign get selectedDesign {
|
||||
return availableDesigns[selectedDesignIndex].withGradient(selectedColor);
|
||||
final gradient = selectedColor;
|
||||
final baseDesign = availableDesigns[selectedDesignIndex];
|
||||
|
||||
CardDesign design;
|
||||
if (CardDesign.preferredColorCombinations.containsKey(gradient)) {
|
||||
design = baseDesign.withGradient(gradient);
|
||||
} else {
|
||||
final specialDesign = availableDesigns.length > 2 ? availableDesigns.last : null;
|
||||
final textColors = specialDesign?.colors ?? baseDesign.colors;
|
||||
design = baseDesign.withGradientAndColorCombination(gradient, textColors);
|
||||
}
|
||||
|
||||
if (design.backgroundType == CardDesignBackgroundTypes.svgIcon &&
|
||||
availableIconPaths.isNotEmpty) {
|
||||
design = design.withIcon(availableIconPaths[selectedIconIndex]);
|
||||
}
|
||||
return design;
|
||||
}
|
||||
|
||||
CardCustomizerState copyWith({
|
||||
@@ -33,6 +53,8 @@ sealed class CardCustomizerState {
|
||||
String? accountName,
|
||||
int? accountIndex,
|
||||
int? cardOrder,
|
||||
List<CardIconPath>? availableIconPaths,
|
||||
int? selectedIconIndex,
|
||||
});
|
||||
|
||||
Gradient get selectedColor => availableColors[selectedColorIndex];
|
||||
@@ -42,7 +64,16 @@ final class CardCustomizerNotLoaded extends CardCustomizerState {
|
||||
CardCustomizerNotLoaded(super.selectedDesignIndex, super.selectedColorIndex, super.availableDesigns, super.availableColors, super.accountName, super.accountIndex, super.displaySats, super.cardOrder);
|
||||
|
||||
@override
|
||||
CardCustomizerState copyWith({int? selectedDesignIndex, int? selectedColorIndex, List<CardDesign>? availableDesigns, List<Gradient>? availableColors, String? accountName, int? accountIndex, int? cardOrder}) {
|
||||
CardCustomizerState copyWith(
|
||||
{int? selectedDesignIndex,
|
||||
int? selectedColorIndex,
|
||||
List<CardDesign>? availableDesigns,
|
||||
List<Gradient>? availableColors,
|
||||
String? accountName,
|
||||
int? accountIndex,
|
||||
int? cardOrder,
|
||||
List<CardIconPath>? availableIconPaths,
|
||||
int? selectedIconIndex}) {
|
||||
// this is never gonna be copied. it's near-instantly replaced with initial
|
||||
throw UnimplementedError();
|
||||
}
|
||||
@@ -56,10 +87,14 @@ final class CardCustomizerInitial extends CardCustomizerState {
|
||||
List<Gradient> availableColors,
|
||||
String accountName,
|
||||
int accountIndex,
|
||||
bool displaySats,
|
||||
int cardOrder,
|
||||
) : super(selectedDesignIndex, selectedColorIndex, availableDesigns, availableColors, accountName,
|
||||
accountIndex, displaySats, cardOrder);
|
||||
bool displaySats,
|
||||
int cardOrder, {
|
||||
List<CardIconPath> availableIconPaths = const [],
|
||||
int selectedIconIndex = 0,
|
||||
}) : super(selectedDesignIndex, selectedColorIndex, availableDesigns,
|
||||
availableColors, accountName, accountIndex, displaySats, cardOrder,
|
||||
availableIconPaths: availableIconPaths,
|
||||
selectedIconIndex: selectedIconIndex);
|
||||
|
||||
CardCustomizerInitial copyWith({
|
||||
int? selectedDesignIndex,
|
||||
@@ -70,6 +105,8 @@ final class CardCustomizerInitial extends CardCustomizerState {
|
||||
int? accountIndex,
|
||||
bool? displaySats,
|
||||
int? cardOrder,
|
||||
List<CardIconPath>? availableIconPaths,
|
||||
int? selectedIconIndex,
|
||||
}) {
|
||||
return CardCustomizerInitial(
|
||||
selectedDesignIndex ?? this.selectedDesignIndex,
|
||||
@@ -80,13 +117,17 @@ final class CardCustomizerInitial extends CardCustomizerState {
|
||||
accountIndex ?? this.accountIndex,
|
||||
displaySats ?? this.displaySats,
|
||||
cardOrder ?? this.cardOrder,
|
||||
availableIconPaths: availableIconPaths ?? this.availableIconPaths,
|
||||
selectedIconIndex: selectedIconIndex ?? this.selectedIconIndex,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final class CardCustomizerSaved extends CardCustomizerState {
|
||||
CardCustomizerSaved(super.selectedDesignIndex, super.selectedColorIndex, super.availableDesigns,
|
||||
super.availableColors, super.accountName, super.accountIndex, super.displaySats, super.cardOrder);
|
||||
CardCustomizerSaved(super.selectedDesignIndex, super.selectedColorIndex,
|
||||
super.availableDesigns, super.availableColors, super.accountName,
|
||||
super.accountIndex, super.displaySats, super.cardOrder,
|
||||
{super.availableIconPaths, super.selectedIconIndex});
|
||||
|
||||
@override
|
||||
CardCustomizerState copyWith(
|
||||
@@ -96,8 +137,10 @@ final class CardCustomizerSaved extends CardCustomizerState {
|
||||
List<Gradient>? availableColors,
|
||||
String? accountName,
|
||||
int? accountIndex,
|
||||
bool? displaySats,
|
||||
int? cardOrder}) {
|
||||
bool? displaySats,
|
||||
int? cardOrder,
|
||||
List<CardIconPath>? availableIconPaths,
|
||||
int? selectedIconIndex}) {
|
||||
return CardCustomizerSaved(
|
||||
selectedDesignIndex ?? this.selectedDesignIndex,
|
||||
selectedColorIndex ?? this.selectedColorIndex,
|
||||
@@ -107,6 +150,8 @@ final class CardCustomizerSaved extends CardCustomizerState {
|
||||
accountIndex ?? this.accountIndex,
|
||||
displaySats ?? this.displaySats,
|
||||
cardOrder ?? this.cardOrder,
|
||||
availableIconPaths: availableIconPaths ?? this.availableIconPaths,
|
||||
selectedIconIndex: selectedIconIndex ?? this.selectedIconIndex,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
import 'package:cw_core/card_design.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
class BalanceCardAction {
|
||||
final String label;
|
||||
@@ -247,16 +246,7 @@ class BalanceCard extends StatelessWidget {
|
||||
switchInCurve: Curves.easeInOut,
|
||||
switchOutCurve: Curves.easeInOut,
|
||||
child: design.backgroundType == CardDesignBackgroundTypes.svgIcon
|
||||
? CakeImageWidget(
|
||||
imageUrl: design.imagePath,
|
||||
key: const ValueKey('svgIcon'),
|
||||
height: iconWidth,
|
||||
width: iconWidth,
|
||||
colorFilter: ColorFilter.mode(
|
||||
design.colors.backgroundImageColor.withAlpha(80),
|
||||
BlendMode.srcIn,
|
||||
),
|
||||
)
|
||||
? _CornerSvgIcon(design: design, iconWidth: iconWidth)
|
||||
: const SizedBox.shrink(
|
||||
key: ValueKey('svgIconOff'),
|
||||
),
|
||||
@@ -320,3 +310,26 @@ class BalanceCard extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _CornerSvgIcon extends StatelessWidget {
|
||||
const _CornerSvgIcon({required this.design, required this.iconWidth});
|
||||
|
||||
final CardDesign design;
|
||||
final double iconWidth;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CakeImageWidget(
|
||||
imageUrl: design.imagePath,
|
||||
key: ValueKey(design.imagePath),
|
||||
height: iconWidth,
|
||||
width: iconWidth,
|
||||
colorFilter: design.preColoredIcon
|
||||
? null
|
||||
: ColorFilter.mode(
|
||||
design.colors.backgroundImageColor.withValues(alpha: 0.33),
|
||||
BlendMode.dstIn,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ class CakeImageWidget extends StatelessWidget {
|
||||
width: width,
|
||||
fit: fit,
|
||||
color: color,
|
||||
errorBuilder: (_, __, ___) => _buildErrorWidget(context),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -52,10 +52,10 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store {
|
||||
final gradients = await _getUsableCardGradients();
|
||||
|
||||
await BalanceCardStyleSettings.fromCardDesign(
|
||||
_wallet.walletInfo.internalId,
|
||||
_moneroAccountList.accounts.length,
|
||||
_moneroAccountList.accounts.length,
|
||||
CardDesign.specialDesignsForCurrencies[_wallet.currency]!
|
||||
walletInfoId: _wallet.walletInfo.internalId,
|
||||
accountIndex: _moneroAccountList.accounts.length,
|
||||
cardOrder: _moneroAccountList.accounts.length,
|
||||
design: CardDesign.specialDesignsForCurrencies[_wallet.currency]!
|
||||
.withGradient(gradients[Random().nextInt(gradients.length)]))
|
||||
.insert();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user