WIP paynym home view

This commit is contained in:
julian 2022-12-21 10:17:53 -06:00
parent a491bfd70f
commit 3b6d53d685
16 changed files with 521 additions and 134 deletions

View file

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 10C0 4.47656 4.47656 0 10 0C15.5234 0 20 4.47656 20 10C20 15.5234 15.5234 20 10 20C4.47656 20 0 15.5234 0 10ZM10 14.375C10.5195 14.375 10.9375 13.957 10.9375 13.4375V10.9375H13.4375C13.957 10.9375 14.375 10.5195 14.375 10C14.375 9.48047 13.957 9.0625 13.4375 9.0625H10.9375V6.5625C10.9375 6.04297 10.5195 5.625 10 5.625C9.48047 5.625 9.0625 6.04297 9.0625 6.5625V9.0625H6.5625C6.04297 9.0625 5.625 9.48047 5.625 10C5.625 10.5195 6.04297 10.9375 6.5625 10.9375H9.0625V13.4375C9.0625 13.957 9.48047 14.375 10 14.375Z" fill="#232323"/>
</svg>

After

Width:  |  Height:  |  Size: 648 B

View file

@ -0,0 +1,48 @@
import 'package:stackwallet/models/paynym/paynym_code.dart';
class PaynymAccount {
final String nymID;
final String nymName;
final List<PaynymCode> codes;
/// list of nymId
final List<String> followers;
/// list of nymId
final List<String> following;
PaynymAccount(
this.nymID,
this.nymName,
this.codes,
this.followers,
this.following,
);
PaynymAccount.fromMap(Map<String, dynamic> map)
: nymID = map["nymID"] as String,
nymName = map["nymName"] as String,
codes = (map["codes"] as List<dynamic>)
.map((e) => PaynymCode.fromMap(Map<String, dynamic>.from(e as Map)))
.toList(),
followers = (map["followers"] as List<dynamic>)
.map((e) => e["nymId"] as String)
.toList(),
following = (map["following"] as List<dynamic>)
.map((e) => e["nymId"] as String)
.toList();
Map<String, dynamic> toMap() => {
"nymID": nymID,
"nymName": nymName,
"codes": codes.map((e) => e.toMap()),
"followers": followers.map((e) => {"nymId": e}),
"following": followers.map((e) => {"nymId": e}),
};
@override
String toString() {
return toMap().toString();
}
}

View file

@ -0,0 +1,27 @@
class PaynymCode {
final bool claimed;
final bool segwit;
final String code;
PaynymCode(
this.claimed,
this.segwit,
this.code,
);
PaynymCode.fromMap(Map<String, dynamic> map)
: claimed = map["claimed"] as bool,
segwit = map["segwit"] as bool,
code = map["code"] as String;
Map<String, dynamic> toMap() => {
"claimed": claimed,
"segwit": segwit,
"code": code,
};
@override
String toString() {
return toMap().toString();
}
}

View file

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -8,6 +9,7 @@ import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/coin_paynym_extension.dart';
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
@ -88,53 +90,52 @@ class _PaynymClaimViewState extends ConsumerState<PaynymClaimView> {
builder: (context) => const ClaimingPaynymDialog(),
).then((value) => shouldCancel = value == true),
);
// generate and submit paynym to api
// ghet wallet to access paynym calls
final wallet = ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as DogecoinWallet;
// get payment code
final pCode = await wallet.getPaymentCode();
final result = await ref
// attempt to create new entry in paynym.is db
final created = await ref
.read(paynymAPIProvider)
.create(pCode.toString());
// final result =
// await ref.read(paynymAPIProvider).token(pCode.toString());
if (created.claimed) {
// payment code already claimed
debugPrint("pcode already claimed!!");
return;
}
// final token =
// "IlBNOFRKWWt1U2RZWEpud0RCcThDaGZpbmZYdjNzcnhoUXJ4M2VvRXdiU3c1MXdNamRvOUpKMkRzeWN3VDNndDN6SFE3Y1YxZ3J2YWJNbW1mMUJ0ajZmWTd0Z2tnU3o5QjhNWnVSM2tqWWZnTUxNVVJKQ1hOIg.FoPF3g.KUMZDC4U_ek-B6cqPLYilXniQv8";
//
// print("======================");
// print(token);
// print(token.codeUnits);
// print(utf8.encode(token));
// print(utf8.decode(token.codeUnits));
//
// print("======================");
//
// final signed = await wallet.signWithNotificationKey(
// Uint8List.fromList(token.codeUnits));
//
// final signedString = Format.uint8listToString(signed);
//
// print("======================");
// print(signed);
// print(signedString);
//
// print("======================");
String token;
// final result2 = await ref
// .read(paynymAPIProvider)
// .claim(token, signedString);
if (created.token == null) {
// payment code already in db
// so we need to fetch a token
// print("======================");
// print(
// result2); // {claimed: PM8TJYkuSdYXJnwDBq8ChfinfXv3srxhQrx3eoEwbSw51wMjdo9JJ2DsycwT3gt3zHQ7cV1grvabMmmf1Btj6fY7tgkgSz9B8MZuR3kjYfgMLMURJCXN, token: IlBNOFRKWWt1U2RZWEpud0RCcThDaGZpbmZYdjNzcnhoUXJ4M2VvRXdiU3c1MXdNamRvOUpKMkRzeWN3VDNndDN6SFE3Y1YxZ3J2YWJNbW1mMUJ0ajZmWTd0Z2tnU3o5QjhNWnVSM2tqWWZnTUxNVVJKQ1hOIg.FoPF3g.KUMZDC4U_ek-B6cqPLYilXniQv8}
// print("======================");
token = await ref
.read(paynymAPIProvider)
.token(pCode.toString());
} else {
token = created.token!;
}
await Future<void>.delayed(const Duration(seconds: 3));
// sign token with notification private key
final signatureBytes = await wallet.signWithNotificationKey(
Uint8List.fromList(token.codeUnits));
final signature = Format.uint8listToString(signatureBytes);
// claim paynym account
final claim =
await ref.read(paynymAPIProvider).claim(token, signature);
if (claim["claimed"] == pCode.toString()) {
// mark claim successful
}
if (mounted && !shouldCancel) {
Navigator.of(context).pop();

View file

@ -1,18 +1,31 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/models/paynym/paynym_account.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/icon_widgets/copy_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
import 'package:stackwallet/widgets/icon_widgets/share_icon.dart';
import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/toggle.dart';
class PaynymHomeView extends StatefulWidget {
const PaynymHomeView({
Key? key,
required this.walletId,
required this.paymentCodeString,
required this.nymAccount,
}) : super(key: key);
final String walletId;
final String paymentCodeString;
final PaynymAccount nymAccount;
static const String routeName = "/paynymHome";
@ -21,6 +34,8 @@ class PaynymHomeView extends StatefulWidget {
}
class _PaynymHomeViewState extends State<PaynymHomeView> {
bool showFollowing = false;
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");
@ -40,14 +55,172 @@ class _PaynymHomeViewState extends State<PaynymHomeView> {
style: STextStyles.navBarTitle(context),
overflow: TextOverflow.ellipsis,
),
actions: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: AspectRatio(
aspectRatio: 1,
child: AppBarIconButton(
icon: SvgPicture.asset(
Assets.svg.circlePlusFilled,
width: 20,
height: 20,
color: Theme.of(context).extension<StackColors>()!.textDark,
),
onPressed: () {
// todo add ?
},
),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: AspectRatio(
aspectRatio: 1,
child: AppBarIconButton(
icon: SvgPicture.asset(
Assets.svg.circleQuestion,
width: 20,
height: 20,
color: Theme.of(context).extension<StackColors>()!.textDark,
),
onPressed: () {
// todo add ?
},
),
),
),
const SizedBox(
width: 4,
),
],
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
PayNymBot(
paymentCodeString: widget.paymentCodeString,
paymentCodeString: widget.nymAccount.codes.first.code,
),
const SizedBox(
height: 10,
),
Text(
widget.nymAccount.nymName,
style: STextStyles.desktopMenuItemSelected(context),
),
const SizedBox(
height: 4,
),
Text(
Format.shorten(widget.nymAccount.codes.first.code, 12, 5),
style: STextStyles.label(context),
),
const SizedBox(
height: 11,
),
Row(
children: [
Expanded(
child: SecondaryButton(
label: "Copy",
buttonHeight: ButtonHeight.l,
iconSpacing: 4,
icon: CopyIcon(
width: 10,
height: 10,
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
onPressed: () {
// copy to clipboard
},
),
),
const SizedBox(
width: 13,
),
Expanded(
child: SecondaryButton(
label: "Share",
buttonHeight: ButtonHeight.l,
iconSpacing: 4,
icon: ShareIcon(
width: 10,
height: 10,
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
onPressed: () {
// copy to clipboard
},
),
),
const SizedBox(
width: 13,
),
Expanded(
child: SecondaryButton(
label: "Address",
buttonHeight: ButtonHeight.l,
iconSpacing: 4,
icon: QrCodeIcon(
width: 10,
height: 10,
color: Theme.of(context)
.extension<StackColors>()!
.textDark,
),
onPressed: () {
// copy to clipboard
},
),
),
],
),
const SizedBox(
height: 24,
),
SizedBox(
height: 40,
child: Toggle(
onColor: Theme.of(context).extension<StackColors>()!.popupBG,
onText: "Following",
offColor: Theme.of(context)
.extension<StackColors>()!
.textFieldDefaultBG,
offText: "Followers",
isOn: showFollowing,
onValueChanged: (value) {
setState(() {
showFollowing = value;
});
},
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
),
),
const SizedBox(
height: 16,
),
RoundedWhiteContainer(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Your PayNym contacts will appear here",
style: STextStyles.label(context),
),
],
),
),
],
),
@ -61,12 +234,30 @@ class PayNymBot extends StatelessWidget {
const PayNymBot({
Key? key,
required this.paymentCodeString,
this.size = 60.0,
}) : super(key: key);
final String paymentCodeString;
final double size;
@override
Widget build(BuildContext context) {
return Image.network("https://paynym.is/$paymentCodeString/avatar");
return ClipRRect(
borderRadius: BorderRadius.circular(size / 2),
child: SizedBox(
width: size,
height: size,
child: Image.network(
"https://paynym.is/$paymentCodeString/avatar",
loadingBuilder: (context, child, event) {
if (event == null) {
return child;
} else {
return const LoadingIndicator();
}
},
),
),
);
}
}

View file

@ -1,10 +1,21 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:stackwallet/pages/paynym/paynym_claim_view.dart';
import 'package:stackwallet/pages/paynym/paynym_home_view.dart';
import 'package:stackwallet/providers/global/paynym_api_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
import 'package:stackwallet/services/coins/coin_paynym_extension.dart';
import 'package:stackwallet/services/coins/dogecoin/dogecoin_wallet.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:tuple/tuple.dart';
import '../../../widgets/loading_indicator.dart';
class WalletNavigationBar extends StatefulWidget {
const WalletNavigationBar({
@ -88,42 +99,84 @@ class _WalletNavigationBarState extends State<WalletNavigationBar> {
AnimatedOpacity(
opacity: scale,
duration: duration,
child: GestureDetector(
onTap: () {
setState(() {
scale = 0;
});
Navigator.of(context).pushNamed(
PaynymClaimView.routeName,
arguments: widget.walletId,
);
},
child: Container(
padding: const EdgeInsets.all(16),
width: 146,
decoration: BoxDecoration(
color:
Theme.of(context).extension<StackColors>()!.popupBG,
boxShadow: [
Theme.of(context)
.extension<StackColors>()!
.standardBoxShadow
],
borderRadius: BorderRadius.circular(
widget.height / 2.0,
child: Consumer(builder: (context, ref, __) {
return GestureDetector(
onTap: () async {
setState(() {
scale = 0;
});
unawaited(
showDialog(
context: context,
builder: (context) => const LoadingIndicator(
width: 100,
),
),
);
// todo make generic and not doge specific
final wallet = (ref
.read(walletsChangeNotifierProvider)
.getManager(widget.walletId)
.wallet as DogecoinWallet);
final code = await wallet.getPaymentCode();
final account = await ref
.read(paynymAPIProvider)
.nym(code.toString());
if (mounted) {
Navigator.of(context).pop();
// check if account exists and for matching code to see if claimed
if (account != null &&
account.codes
.where((e) =>
e.code == code.toString() && e.claimed)
.isNotEmpty) {
await Navigator.of(context).pushNamed(
PaynymHomeView.routeName,
arguments: Tuple2(
widget.walletId,
account,
),
);
} else {
await Navigator.of(context).pushNamed(
PaynymClaimView.routeName,
arguments: widget.walletId,
);
}
}
},
child: Container(
padding: const EdgeInsets.all(16),
width: 146,
decoration: BoxDecoration(
color:
Theme.of(context).extension<StackColors>()!.popupBG,
boxShadow: [
Theme.of(context)
.extension<StackColors>()!
.standardBoxShadow
],
borderRadius: BorderRadius.circular(
widget.height / 2.0,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Paynym",
style: STextStyles.w600_12(context),
),
],
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Paynym",
style: STextStyles.w600_12(context),
),
],
),
),
),
);
}),
),
const SizedBox(
height: 8,

View file

@ -6,6 +6,7 @@ import 'package:stackwallet/models/contact_address_entry.dart';
import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
import 'package:stackwallet/models/paymint/transactions_model.dart';
import 'package:stackwallet/models/paynym/paynym_account.dart';
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart';
import 'package:stackwallet/pages/add_wallet_views/create_or_restore_wallet_view/create_or_restore_wallet_view.dart';
@ -204,12 +205,12 @@ class RouteGenerator {
return _routeError("${settings.name} invalid args: ${args.toString()}");
case PaynymHomeView.routeName:
if (args is Tuple2<String, String>) {
if (args is Tuple2<String, PaynymAccount>) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => PaynymHomeView(
walletId: args.item1,
paymentCodeString: args.item2,
nymAccount: args.item2,
),
settings: RouteSettings(
name: settings.name,

View file

@ -34,4 +34,7 @@ extension PayNym on DogecoinWallet {
final signed = pair.sign(SHA256Digest().process(data));
return signed;
}
// Future<Map<String, dynamic>> prepareNotificationTransaction(
// String targetPaymentCode) async {}
}

View file

@ -76,6 +76,7 @@ class _SVG {
String get circleSliders => "assets/svg/configuration.svg";
String get circlePlus => "assets/svg/plus-circle.svg";
String get circlePlusFilled => "assets/svg/circle-plus-filled.svg";
String get framedGear => "assets/svg/framed-gear.svg";
String get framedAddressBook => "assets/svg/framed-address-book.svg";
String get circleNode => "assets/svg/node-circle.svg";

View file

@ -8,6 +8,10 @@ import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
abstract class Format {
static String shorten(String value, int beginCount, int endCount) {
return "${value.substring(0, beginCount)}...${value.substring(value.length - endCount)}";
}
static Decimal satoshisToAmount(int sats, {required Coin coin}) {
return (Decimal.fromInt(sats) /
Decimal.fromInt(Constants.satsPerCoin(coin)))

View file

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:stackwallet/models/paynym/created_paynym.dart';
import 'package:stackwallet/models/paynym/paynym_account.dart';
class PaynymAPI {
static const String baseURL = "https://paynym.is/api";
@ -117,8 +118,9 @@ class PaynymAPI {
//
//
// ------
Future<Map<String, dynamic>> token(String code) async {
return _post("/token", {"code": code});
Future<String> token(String code) async {
final map = await _post("/token", {"code": code});
return map["token"] as String;
}
// ### `/api/v1/nym`
@ -172,8 +174,13 @@ class PaynymAPI {
// | 404 | Nym not found |
// | 400 | Bad request |
Future<Map<String, dynamic>> nym(String code) async {
return _post("/nym", {"code": code});
Future<PaynymAccount?> nym(String code) async {
final map = await _post("/nym", {"nym": code});
try {
return PaynymAccount.fromMap(map);
} catch (_) {
return null;
}
}
// ## Authenticated Requests

View file

@ -16,6 +16,7 @@ class SecondaryButton extends StatelessWidget {
this.onPressed,
this.enabled = true,
this.buttonHeight,
this.iconSpacing = 10,
}) : super(key: key);
final double? width;
@ -25,6 +26,7 @@ class SecondaryButton extends StatelessWidget {
final bool enabled;
final Widget? icon;
final ButtonHeight? buttonHeight;
final double iconSpacing;
TextStyle getStyle(bool isDesktop, BuildContext context) {
if (isDesktop) {
@ -66,6 +68,16 @@ class SecondaryButton extends StatelessWidget {
: STextStyles.desktopButtonSecondaryDisabled(context);
}
} else {
if (buttonHeight == ButtonHeight.l) {
return STextStyles.button(context).copyWith(
fontSize: 10,
color: enabled
? Theme.of(context).extension<StackColors>()!.buttonTextSecondary
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondaryDisabled,
);
}
return STextStyles.button(context).copyWith(
color: enabled
? Theme.of(context).extension<StackColors>()!.buttonTextSecondary
@ -136,8 +148,8 @@ class SecondaryButton extends StatelessWidget {
children: [
if (icon != null) icon!,
if (icon != null && label != null)
const SizedBox(
width: 10,
SizedBox(
width: iconSpacing,
),
if (label != null)
Text(

View file

@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
class ShareIcon extends StatelessWidget {
const ShareIcon({
Key? key,
this.width = 18,
this.height = 18,
this.color,
}) : super(key: key);
final double width;
final double height;
final Color? color;
@override
Widget build(BuildContext context) {
return SvgPicture.asset(
Assets.svg.share,
width: width,
height: height,
color: color ?? Theme.of(context).extension<StackColors>()!.textDark3,
);
}
}

View file

@ -1,6 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import 'package:stackwallet/utilities/assets.dart';
class LoadingIndicator extends StatelessWidget {
@ -15,13 +14,18 @@ class LoadingIndicator extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
height: height,
child: Lottie.asset(
Assets.lottie.test2,
animate: true,
repeat: true,
return Container(
color: Colors.transparent,
child: Center(
child: SizedBox(
width: width,
height: height,
child: Lottie.asset(
Assets.lottie.test2,
animate: true,
repeat: true,
),
),
),
);
}

View file

@ -186,29 +186,31 @@ class ToggleState extends State<Toggle> {
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
widget.onIcon ?? "",
width: 12,
height: 14,
color: isDesktop
? !_isOn
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary
: !_isOn
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
const SizedBox(
width: 5,
),
if (widget.onIcon != null)
SvgPicture.asset(
widget.onIcon ?? "",
width: 12,
height: 14,
color: isDesktop
? !_isOn
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary
: !_isOn
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
if (widget.onIcon != null)
const SizedBox(
width: 5,
),
Text(
widget.onText ?? "",
style: isDesktop
@ -243,29 +245,31 @@ class ToggleState extends State<Toggle> {
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SvgPicture.asset(
widget.offIcon ?? "",
width: 12,
height: 14,
color: isDesktop
? _isOn
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary
: _isOn
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
const SizedBox(
width: 5,
),
if (widget.offIcon != null)
SvgPicture.asset(
widget.offIcon ?? "",
width: 12,
height: 14,
color: isDesktop
? _isOn
? Theme.of(context)
.extension<StackColors>()!
.accentColorBlue
: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary
: _isOn
? Theme.of(context)
.extension<StackColors>()!
.textDark
: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
if (widget.offIcon != null)
const SizedBox(
width: 5,
),
Text(
widget.offText ?? "",
style: isDesktop

View file

@ -305,6 +305,7 @@ flutter:
- assets/svg/keys.svg
- assets/svg/arrow-down.svg
- assets/svg/plus-circle.svg
- assets/svg/circle-plus-filled.svg
- assets/svg/configuration.svg
# coin icons
- assets/svg/coin_icons/Bitcoin.svg