mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2025-03-15 16:12:16 +00:00
AddressBookAddressChooser search and ui tweaks
This commit is contained in:
parent
712b33833b
commit
fc981ef6e0
7 changed files with 197 additions and 13 deletions
3
assets/svg/chevron-up.svg
Normal file
3
assets/svg/chevron-up.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="12" height="7" viewBox="0 0 12 7" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 6L6 1L1 6" stroke="#8E9192" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 208 B |
|
@ -1,10 +1,18 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:stackwallet/models/contact.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/sub_widgets/contact_list_item.dart';
|
||||
import 'package:stackwallet/providers/global/address_book_service_provider.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.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:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
class AddressBookAddressChooser extends StatefulWidget {
|
||||
const AddressBookAddressChooser({
|
||||
|
@ -20,6 +28,12 @@ class AddressBookAddressChooser extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
|
||||
late final bool isDesktop;
|
||||
late final TextEditingController _searchController;
|
||||
late final FocusNode searchFieldFocusNode;
|
||||
|
||||
String _searchTerm = "";
|
||||
|
||||
int _compareContactFavorite(Contact a, Contact b) {
|
||||
if (a.isFavorite && b.isFavorite) {
|
||||
return 0;
|
||||
|
@ -43,29 +57,128 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
|
|||
return favorites;
|
||||
}
|
||||
|
||||
List<Contact> filter(List<Contact> contacts) {
|
||||
List<Contact> filter(List<Contact> contacts, String searchTerm) {
|
||||
if (widget.coin != null) {
|
||||
contacts.removeWhere(
|
||||
(e) => e.addresses.where((a) => a.coin == widget.coin!).isEmpty);
|
||||
}
|
||||
|
||||
contacts.retainWhere((e) => _matches(searchTerm, e));
|
||||
|
||||
if (contacts.length < 2) {
|
||||
return contacts;
|
||||
}
|
||||
|
||||
// redundant due to pullOutFavorites?
|
||||
contacts.sort(_compareContactFavorite);
|
||||
|
||||
// TODO: other filtering?
|
||||
|
||||
return contacts;
|
||||
}
|
||||
|
||||
bool _matches(String term, Contact contact) {
|
||||
final text = term.toLowerCase();
|
||||
if (contact.name.toLowerCase().contains(text)) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < contact.addresses.length; i++) {
|
||||
if (contact.addresses[i].label.toLowerCase().contains(text) ||
|
||||
contact.addresses[i].coin.name.toLowerCase().contains(text) ||
|
||||
contact.addresses[i].coin.prettyName.toLowerCase().contains(text) ||
|
||||
contact.addresses[i].coin.ticker.toLowerCase().contains(text) ||
|
||||
contact.addresses[i].address.toLowerCase().contains(text)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
isDesktop = Util.isDesktop;
|
||||
searchFieldFocusNode = FocusNode();
|
||||
_searchController = TextEditingController();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.dispose();
|
||||
searchFieldFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
// search field
|
||||
const TextField(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 32,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autocorrect: !isDesktop,
|
||||
enableSuggestions: !isDesktop,
|
||||
controller: _searchController,
|
||||
focusNode: searchFieldFocusNode,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_searchTerm = value;
|
||||
});
|
||||
},
|
||||
style: isDesktop
|
||||
? STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
height: 1.8,
|
||||
)
|
||||
: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Search",
|
||||
searchFieldFocusNode,
|
||||
context,
|
||||
desktopMed: isDesktop,
|
||||
).copyWith(
|
||||
prefixIcon: Padding(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: isDesktop ? 12 : 10,
|
||||
vertical: isDesktop ? 18 : 16,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.search,
|
||||
width: isDesktop ? 20 : 16,
|
||||
height: isDesktop ? 20 : 16,
|
||||
),
|
||||
),
|
||||
suffixIcon: _searchController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
_searchController.text = "";
|
||||
_searchTerm = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
|
@ -83,7 +196,7 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
|
|||
.select((value) => value.contacts))
|
||||
.toList();
|
||||
|
||||
contacts = filter(contacts);
|
||||
contacts = filter(contacts, _searchTerm);
|
||||
|
||||
final favorites = pullOutFavorites(contacts);
|
||||
|
||||
|
@ -96,6 +209,8 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
|
|||
itemBuilder: (context, index) {
|
||||
if (index == 0) {
|
||||
return Padding(
|
||||
key: const Key(
|
||||
"addressBookCAddressChooserFavoritesHeaderItemKey"),
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 10,
|
||||
),
|
||||
|
@ -108,12 +223,14 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
|
|||
} else if (index <= favorites.length) {
|
||||
final id = favorites[index - 1].id;
|
||||
return ContactListItem(
|
||||
key: Key("contactCard_${id}_key"),
|
||||
key: Key("contactContactListItem_${id}_key"),
|
||||
contactId: id,
|
||||
filterByCoin: widget.coin,
|
||||
);
|
||||
} else if (index == favorites.length + 1) {
|
||||
return Padding(
|
||||
key: const Key(
|
||||
"addressBookCAddressChooserAllContactsHeaderItemKey"),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10,
|
||||
),
|
||||
|
@ -126,7 +243,7 @@ class _AddressBookAddressChooserState extends State<AddressBookAddressChooser> {
|
|||
} else {
|
||||
final id = contacts[index - favorites.length - 1].id;
|
||||
return ContactListItem(
|
||||
key: Key("contactCard_${id}_key"),
|
||||
key: Key("contactContactListItem_${id}_key"),
|
||||
contactId: id,
|
||||
filterByCoin: widget.coin,
|
||||
);
|
||||
|
|
|
@ -10,7 +10,7 @@ import 'package:stackwallet/widgets/expandable.dart';
|
|||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart';
|
||||
|
||||
class ContactListItem extends ConsumerWidget {
|
||||
class ContactListItem extends ConsumerStatefulWidget {
|
||||
const ContactListItem({
|
||||
Key? key,
|
||||
required this.contactId,
|
||||
|
@ -21,7 +21,24 @@ class ContactListItem extends ConsumerWidget {
|
|||
final Coin? filterByCoin;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
ConsumerState<ContactListItem> createState() => _ContactListItemState();
|
||||
}
|
||||
|
||||
class _ContactListItemState extends ConsumerState<ContactListItem> {
|
||||
late final String contactId;
|
||||
late final Coin? filterByCoin;
|
||||
|
||||
ExpandableState _state = ExpandableState.collapsed;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
contactId = widget.contactId;
|
||||
filterByCoin = widget.filterByCoin;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final contact = ref.watch(addressBookServiceProvider
|
||||
.select((value) => value.getContactById(contactId)));
|
||||
|
||||
|
@ -29,6 +46,11 @@ class ContactListItem extends ConsumerWidget {
|
|||
padding: const EdgeInsets.all(0),
|
||||
borderColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
child: Expandable(
|
||||
onExpandChanged: (state) {
|
||||
setState(() {
|
||||
_state = state;
|
||||
});
|
||||
},
|
||||
header: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
|
@ -36,6 +58,7 @@ class ContactListItem extends ConsumerWidget {
|
|||
),
|
||||
child: AddressBookCard(
|
||||
contactId: contactId,
|
||||
indicatorDown: _state == ExpandableState.expanded,
|
||||
),
|
||||
),
|
||||
body: Column(
|
||||
|
|
|
@ -1190,8 +1190,32 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
builder: (context) => DesktopDialog(
|
||||
maxWidth: 696,
|
||||
maxHeight: 600,
|
||||
child: AddressBookAddressChooser(
|
||||
coin: coin,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
),
|
||||
child: Text(
|
||||
"Address book",
|
||||
style:
|
||||
STextStyles.desktopH3(context),
|
||||
),
|
||||
),
|
||||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: AddressBookAddressChooser(
|
||||
coin: coin,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -96,6 +96,7 @@ class _SVG {
|
|||
String get qrcode => "assets/svg/qrcode1.svg";
|
||||
String get ellipsis => "assets/svg/gear-3.svg";
|
||||
String get chevronDown => "assets/svg/chevron-down.svg";
|
||||
String get chevronUp => "assets/svg/chevron-up.svg";
|
||||
String get swap => "assets/svg/swap.svg";
|
||||
String get downloadFolder => "assets/svg/folder-down.svg";
|
||||
String get lock => "assets/svg/lock-keyhole.svg";
|
||||
|
|
|
@ -13,9 +13,14 @@ import 'package:stackwallet/widgets/conditional_parent.dart';
|
|||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
class AddressBookCard extends ConsumerStatefulWidget {
|
||||
const AddressBookCard({Key? key, required this.contactId}) : super(key: key);
|
||||
const AddressBookCard({
|
||||
Key? key,
|
||||
required this.contactId,
|
||||
this.indicatorDown,
|
||||
}) : super(key: key);
|
||||
|
||||
final String contactId;
|
||||
final bool? indicatorDown;
|
||||
|
||||
@override
|
||||
ConsumerState<AddressBookCard> createState() => _AddressBookCardState();
|
||||
|
@ -122,7 +127,17 @@ class _AddressBookCardState extends ConsumerState<AddressBookCard> {
|
|||
style: STextStyles.label(context),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
if (isDesktop) const Spacer(),
|
||||
if (isDesktop)
|
||||
SvgPicture.asset(
|
||||
widget.indicatorDown == true
|
||||
? Assets.svg.chevronDown
|
||||
: Assets.svg.chevronUp,
|
||||
width: 10,
|
||||
height: 5,
|
||||
color: Theme.of(context).extension<StackColors>()!.textSubtitle2,
|
||||
),
|
||||
],
|
||||
),
|
||||
builder: (child) => RoundedWhiteContainer(
|
||||
|
|
|
@ -232,6 +232,7 @@ flutter:
|
|||
- assets/svg/gear-3.svg
|
||||
- assets/svg/swap.svg
|
||||
- assets/svg/chevron-down.svg
|
||||
- assets/svg/chevron-up.svg
|
||||
- assets/svg/lock-keyhole.svg
|
||||
- assets/svg/rotate-exclamation.svg
|
||||
- assets/svg/folder-down.svg
|
||||
|
|
Loading…
Reference in a new issue