AddressBookAddressChooser search and ui tweaks

This commit is contained in:
julian 2022-11-02 10:01:03 -06:00
parent 712b33833b
commit fc981ef6e0
7 changed files with 197 additions and 13 deletions

View 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

View file

@ -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,
);

View file

@ -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(

View file

@ -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,
),
),
],
),
),
);

View file

@ -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";

View file

@ -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(

View file

@ -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