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:
David Adegoke
2026-05-31 13:29:23 +02:00
committed by GitHub
co-authored by tuxsudo
parent 997201e080
commit 0c3b06d3c1
115 changed files with 2124 additions and 328 deletions
+4 -4
View File
@@ -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();
}
}
+287 -172
View File
@@ -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,
),
);
}
}
+1
View File
@@ -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();
}