mobile address list changes as per figma

This commit is contained in:
julian 2024-04-24 17:17:29 -06:00
parent 53b849c8c5
commit 420c73ed5d
4 changed files with 394 additions and 140 deletions

View file

@ -10,31 +10,46 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:ui' as ui;
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:path_provider/path_provider.dart';
import 'package:qr_flutter/qr_flutter.dart';
import 'package:share_plus/share_plus.dart';
import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/pages/receive_view/addresses/address_tag.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/themes/coin_icon_provider.dart'; import 'package:stackwallet/themes/coin_icon_provider.dart';
import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/address_utils.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/clipboard_interface.dart'; import 'package:stackwallet/utilities/clipboard_interface.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/conditional_parent.dart'; import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/custom_buttons/simple_edit_button.dart';
import 'package:stackwallet/widgets/desktop/primary_button.dart';
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
class AddressCard extends ConsumerStatefulWidget { class AddressCard extends ConsumerStatefulWidget {
const AddressCard({ const AddressCard({
Key? key, super.key,
required this.addressId, required this.addressId,
required this.walletId, required this.walletId,
required this.coin, required this.coin,
this.onPressed, this.onPressed,
this.clipboard = const ClipboardWrapper(), this.clipboard = const ClipboardWrapper(),
}) : super(key: key); });
final int addressId; final int addressId;
final String walletId; final String walletId;
@ -47,6 +62,7 @@ class AddressCard extends ConsumerStatefulWidget {
} }
class _AddressCardState extends ConsumerState<AddressCard> { class _AddressCardState extends ConsumerState<AddressCard> {
final _qrKey = GlobalKey();
final isDesktop = Util.isDesktop; final isDesktop = Util.isDesktop;
late Stream<AddressLabel?> stream; late Stream<AddressLabel?> stream;
@ -54,6 +70,72 @@ class _AddressCardState extends ConsumerState<AddressCard> {
AddressLabel? label; AddressLabel? label;
Future<void> _capturePng(bool shouldSaveInsteadOfShare) async {
try {
final RenderRepaintBoundary boundary =
_qrKey.currentContext?.findRenderObject() as RenderRepaintBoundary;
final ui.Image image = await boundary.toImage();
final ByteData? byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
final Uint8List pngBytes = byteData!.buffer.asUint8List();
if (shouldSaveInsteadOfShare) {
if (Util.isDesktop) {
final dir = Directory("${Platform.environment['HOME']}");
if (!dir.existsSync()) {
throw Exception(
"Home dir not found while trying to open filepicker on QR image save");
}
final path = await FilePicker.platform.saveFile(
fileName: "qrcode.png",
initialDirectory: dir.path,
);
if (path != null) {
final file = File(path);
if (file.existsSync()) {
if (mounted) {
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: "$path already exists!",
context: context,
),
);
}
} else {
await file.writeAsBytes(pngBytes);
if (mounted) {
unawaited(
showFloatingFlushBar(
type: FlushBarType.success,
message: "$path saved!",
context: context,
),
);
}
}
}
} else {
// await DocumentFileSavePlus.saveFile(
// pngBytes,
// "receive_qr_code_${DateTime.now().toLocal().toIso8601String()}.png",
// "image/png");
}
} else {
final tempDir = await getTemporaryDirectory();
final file = await File("${tempDir.path}/qrcode.png").create();
await file.writeAsBytes(pngBytes);
await Share.shareFiles(["${tempDir.path}/qrcode.png"],
text: "Receive URI QR Code");
}
} catch (e) {
//todo: comeback to this
debugPrint(e.toString());
}
}
@override @override
void initState() { void initState() {
address = MainDB.instance.isar.addresses address = MainDB.instance.isar.addresses
@ -117,16 +199,32 @@ class _AddressCardState extends ConsumerState<AddressCard> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (label!.value.isNotEmpty) Row(
Text( mainAxisAlignment: MainAxisAlignment.spaceBetween,
label!.value, children: [
style: STextStyles.itemSubtitle(context), Text(
textAlign: TextAlign.left, label!.value.isNotEmpty ? label!.value : "No label",
), style: STextStyles.itemSubtitle(context),
if (label!.value.isNotEmpty) textAlign: TextAlign.left,
SizedBox( ),
height: isDesktop ? 2 : 8, SimpleEditButton(
), editValue: label!.value,
editLabel: 'label',
overrideTitle: 'Edit label',
disableIcon: true,
onValueChanged: (value) {
MainDB.instance.putAddressLabel(
label!.copyWith(
label: value,
),
);
},
),
],
),
SizedBox(
height: isDesktop ? 2 : 8,
),
Row( Row(
children: [ children: [
Expanded( Expanded(
@ -140,18 +238,152 @@ class _AddressCardState extends ConsumerState<AddressCard> {
const SizedBox( const SizedBox(
height: 10, height: 10,
), ),
if (label!.tags != null && label!.tags!.isNotEmpty) Row(
Wrap( children: [
spacing: 10, CustomTextButton(
runSpacing: 10, text: "Copy address",
children: label!.tags! onTap: () {
.map( widget.clipboard
(e) => AddressTag( .setData(
tag: e, ClipboardData(
text: address.value,
), ),
) )
.toList(), .then((value) {
), if (context.mounted) {
unawaited(
showFloatingFlushBar(
type: FlushBarType.info,
message: "Copied to clipboard",
context: context,
),
);
}
});
},
),
const SizedBox(
width: 16,
),
CustomTextButton(
text: "Show QR code",
onTap: () async {
await showDialog<void>(
context: context,
builder: (_) {
return StackDialogBase(
child: Column(
children: [
if (label!.value.isNotEmpty)
Text(
label!.value,
style: STextStyles.w600_18(context),
),
if (label!.value.isNotEmpty)
const SizedBox(
height: 8,
),
Text(
address.value,
style:
STextStyles.w500_16(context).copyWith(
color: Theme.of(context)
.extension<StackColors>()!
.textSubtitle1,
),
),
const SizedBox(
height: 16,
),
Center(
child: RepaintBoundary(
key: _qrKey,
child: QrImageView(
data: AddressUtils.buildUriString(
widget.coin,
address.value,
{},
),
size: 220,
backgroundColor: Theme.of(context)
.extension<StackColors>()!
.popupBG,
foregroundColor: Theme.of(context)
.extension<StackColors>()!
.accentColorDark,
),
),
),
const SizedBox(
height: 16,
),
Row(
children: [
if (!isDesktop)
Expanded(
child: SecondaryButton(
label: "Share",
buttonHeight: isDesktop
? ButtonHeight.l
: null,
icon: SvgPicture.asset(
Assets.svg.share,
width: 14,
height: 14,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextSecondary,
),
onPressed: () async {
await _capturePng(false);
},
),
),
if (isDesktop)
Expanded(
child: PrimaryButton(
buttonHeight: isDesktop
? ButtonHeight.l
: null,
onPressed: () async {
// TODO: add save functionality instead of share
// save works on linux at the moment
await _capturePng(true);
},
label: "Save",
icon: SvgPicture.asset(
Assets.svg.arrowDown,
width: 20,
height: 20,
color: Theme.of(context)
.extension<StackColors>()!
.buttonTextPrimary,
),
),
),
],
)
],
),
);
},
);
},
),
],
),
// if (label!.tags != null && label!.tags!.isNotEmpty)
// Wrap(
// spacing: 10,
// runSpacing: 10,
// children: label!.tags!
// .map(
// (e) => AddressTag(
// tag: e,
// ),
// )
// .toList(),
// ),
], ],
), ),
); );

View file

@ -10,14 +10,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:stackwallet/db/isar/main_db.dart'; import 'package:stackwallet/db/isar/main_db.dart';
import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/isar/models/isar_models.dart';
import 'package:stackwallet/pages/receive_view/addresses/address_card.dart'; import 'package:stackwallet/pages/receive_view/addresses/address_card.dart';
import 'package:stackwallet/pages/receive_view/addresses/address_details_view.dart'; import 'package:stackwallet/pages/receive_view/addresses/address_details_view.dart';
import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart'; import 'package:stackwallet/wallets/isar/providers/wallet_info_provider.dart';
@ -25,13 +23,8 @@ import 'package:stackwallet/widgets/background.dart';
import 'package:stackwallet/widgets/conditional_parent.dart'; import 'package:stackwallet/widgets/conditional_parent.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/loading_indicator.dart'; import 'package:stackwallet/widgets/loading_indicator.dart';
import 'package:stackwallet/widgets/stack_text_field.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
import '../../../utilities/assets.dart';
import '../../../widgets/icon_widgets/x_icon.dart';
import '../../../widgets/textfield_icon_button.dart';
class WalletAddressesView extends ConsumerStatefulWidget { class WalletAddressesView extends ConsumerStatefulWidget {
const WalletAddressesView({ const WalletAddressesView({
Key? key, Key? key,
@ -50,10 +43,10 @@ class WalletAddressesView extends ConsumerStatefulWidget {
class _WalletAddressesViewState extends ConsumerState<WalletAddressesView> { class _WalletAddressesViewState extends ConsumerState<WalletAddressesView> {
final bool isDesktop = Util.isDesktop; final bool isDesktop = Util.isDesktop;
String _searchString = ""; final String _searchString = "";
late final TextEditingController _searchController; // late final TextEditingController _searchController;
final searchFieldFocusNode = FocusNode(); // final searchFieldFocusNode = FocusNode();
Future<List<int>> _search(String term) async { Future<List<int>> _search(String term) async {
if (term.isEmpty) { if (term.isEmpty) {
@ -119,19 +112,19 @@ class _WalletAddressesViewState extends ConsumerState<WalletAddressesView> {
.findAll(); .findAll();
} }
@override // @override
void initState() { // void initState() {
_searchController = TextEditingController(); // _searchController = TextEditingController();
//
super.initState(); // super.initState();
} // }
//
@override // @override
void dispose() { // void dispose() {
_searchController.dispose(); // _searchController.dispose();
searchFieldFocusNode.dispose(); // searchFieldFocusNode.dispose();
super.dispose(); // super.dispose();
} // }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -165,74 +158,74 @@ class _WalletAddressesViewState extends ConsumerState<WalletAddressesView> {
), ),
child: Column( child: Column(
children: [ children: [
SizedBox( // SizedBox(
width: isDesktop ? 490 : null, // width: isDesktop ? 490 : null,
child: ClipRRect( // child: ClipRRect(
borderRadius: BorderRadius.circular( // borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius, // Constants.size.circularBorderRadius,
), // ),
child: TextField( // child: TextField(
autocorrect: !isDesktop, // autocorrect: !isDesktop,
enableSuggestions: !isDesktop, // enableSuggestions: !isDesktop,
controller: _searchController, // controller: _searchController,
focusNode: searchFieldFocusNode, // focusNode: searchFieldFocusNode,
onChanged: (value) { // onChanged: (value) {
setState(() { // setState(() {
_searchString = value; // _searchString = value;
}); // });
}, // },
style: isDesktop // style: isDesktop
? STextStyles.desktopTextExtraSmall(context).copyWith( // ? STextStyles.desktopTextExtraSmall(context).copyWith(
color: Theme.of(context) // color: Theme.of(context)
.extension<StackColors>()! // .extension<StackColors>()!
.textFieldActiveText, // .textFieldActiveText,
height: 1.8, // height: 1.8,
) // )
: STextStyles.field(context), // : STextStyles.field(context),
decoration: standardInputDecoration( // decoration: standardInputDecoration(
"Search...", // "Search...",
searchFieldFocusNode, // searchFieldFocusNode,
context, // context,
desktopMed: isDesktop, // desktopMed: isDesktop,
).copyWith( // ).copyWith(
prefixIcon: Padding( // prefixIcon: Padding(
padding: EdgeInsets.symmetric( // padding: EdgeInsets.symmetric(
horizontal: isDesktop ? 12 : 10, // horizontal: isDesktop ? 12 : 10,
vertical: isDesktop ? 18 : 16, // vertical: isDesktop ? 18 : 16,
), // ),
child: SvgPicture.asset( // child: SvgPicture.asset(
Assets.svg.search, // Assets.svg.search,
width: isDesktop ? 20 : 16, // width: isDesktop ? 20 : 16,
height: isDesktop ? 20 : 16, // height: isDesktop ? 20 : 16,
), // ),
), // ),
suffixIcon: _searchController.text.isNotEmpty // suffixIcon: _searchController.text.isNotEmpty
? Padding( // ? Padding(
padding: const EdgeInsets.only(right: 0), // padding: const EdgeInsets.only(right: 0),
child: UnconstrainedBox( // child: UnconstrainedBox(
child: Row( // child: Row(
children: [ // children: [
TextFieldIconButton( // TextFieldIconButton(
child: const XIcon(), // child: const XIcon(),
onTap: () async { // onTap: () async {
setState(() { // setState(() {
_searchController.text = ""; // _searchController.text = "";
_searchString = ""; // _searchString = "";
}); // });
}, // },
), // ),
], // ],
), // ),
), // ),
) // )
: null, // : null,
), // ),
), // ),
), // ),
), // ),
SizedBox( // SizedBox(
height: isDesktop ? 20 : 16, // height: isDesktop ? 20 : 16,
), // ),
Expanded( Expanded(
child: FutureBuilder( child: FutureBuilder(
future: _search(_searchString), future: _search(_searchString),
@ -249,15 +242,17 @@ class _WalletAddressesViewState extends ConsumerState<WalletAddressesView> {
walletId: widget.walletId, walletId: widget.walletId,
addressId: snapshot.data![index], addressId: snapshot.data![index],
coin: coin, coin: coin,
onPressed: () { onPressed: !isDesktop
Navigator.of(context).pushNamed( ? null
AddressDetailsView.routeName, : () {
arguments: Tuple2( Navigator.of(context).pushNamed(
snapshot.data![index], AddressDetailsView.routeName,
widget.walletId, arguments: Tuple2(
), snapshot.data![index],
); widget.walletId,
}, ),
);
},
), ),
); );
} else { } else {

View file

@ -317,6 +317,28 @@ class STextStyles {
} }
} }
static TextStyle w600_18(BuildContext context) {
switch (_theme(context).themeId) {
default:
return GoogleFonts.inter(
color: _theme(context).textDark,
fontWeight: FontWeight.w600,
fontSize: 18,
);
}
}
static TextStyle w500_16(BuildContext context) {
switch (_theme(context).themeId) {
default:
return GoogleFonts.inter(
color: _theme(context).textDark,
fontWeight: FontWeight.w500,
fontSize: 16,
);
}
}
static TextStyle w500_14(BuildContext context) { static TextStyle w500_14(BuildContext context) {
switch (_theme(context).themeId) { switch (_theme(context).themeId) {
default: default:

View file

@ -15,29 +15,31 @@ import 'package:stackwallet/themes/stack_colors.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/util.dart'; import 'package:stackwallet/utilities/util.dart';
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
import 'package:stackwallet/widgets/icon_widgets/pencil_icon.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
import '../desktop/desktop_dialog.dart';
import '../icon_widgets/pencil_icon.dart';
class SimpleEditButton extends StatelessWidget { class SimpleEditButton extends StatelessWidget {
const SimpleEditButton({ const SimpleEditButton({
Key? key, super.key,
this.editValue, this.editValue,
this.editLabel, this.editLabel,
this.overrideTitle,
this.disableIcon = false,
this.onValueChanged, this.onValueChanged,
this.onPressedOverride, this.onPressedOverride,
}) : assert( }) : assert(
(editLabel != null && editValue != null && onValueChanged != null) || (editLabel != null && editValue != null && onValueChanged != null) ||
(editLabel == null && (editLabel == null &&
editValue == null && editValue == null &&
onValueChanged == null && onValueChanged == null &&
onPressedOverride != null), onPressedOverride != null),
), );
super(key: key);
final String? editValue; final String? editValue;
final String? editLabel; final String? editLabel;
final String? overrideTitle;
final bool disableIcon;
final void Function(String)? onValueChanged; final void Function(String)? onValueChanged;
final VoidCallback? onPressedOverride; final VoidCallback? onPressedOverride;
@ -101,17 +103,20 @@ class SimpleEditButton extends StatelessWidget {
}, },
child: Row( child: Row(
children: [ children: [
SvgPicture.asset( if (!disableIcon)
Assets.svg.pencil, SvgPicture.asset(
width: 10, Assets.svg.pencil,
height: 10, width: 10,
color: Theme.of(context).extension<StackColors>()!.infoItemIcons, height: 10,
), color:
const SizedBox( Theme.of(context).extension<StackColors>()!.infoItemIcons,
width: 4, ),
), if (!disableIcon)
const SizedBox(
width: 4,
),
Text( Text(
"Edit", overrideTitle ?? "Edit",
style: STextStyles.link2(context), style: STextStyles.link2(context),
), ),
], ],