Autocomplete container UI changes (#6690)

* add left & right padding to autocomplete peer and format

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

* handle max height for autocomplete equal padding, add box shadow and format

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>

---------

Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
This commit is contained in:
Sahil Yeole 2023-12-17 06:52:18 +05:30 committed by GitHub
parent 79411430a5
commit c168b7e979
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 309 additions and 239 deletions

View File

@ -6,9 +6,9 @@ import 'package:flutter_hbb/models/peer_model.dart';
import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/common.dart';
import 'package:flutter_hbb/common/widgets/peer_card.dart'; import 'package:flutter_hbb/common/widgets/peer_card.dart';
Future<List<Peer>> getAllPeers() async { Future<List<Peer>> getAllPeers() async {
Map<String, dynamic> recentPeers = jsonDecode(await bind.mainLoadRecentPeersSync()); Map<String, dynamic> recentPeers =
jsonDecode(await bind.mainLoadRecentPeersSync());
Map<String, dynamic> lanPeers = jsonDecode(await bind.mainLoadLanPeersSync()); Map<String, dynamic> lanPeers = jsonDecode(await bind.mainLoadLanPeersSync());
Map<String, dynamic> abPeers = jsonDecode(await bind.mainLoadAbSync()); Map<String, dynamic> abPeers = jsonDecode(await bind.mainLoadAbSync());
Map<String, dynamic> groupPeers = jsonDecode(await bind.mainLoadGroupSync()); Map<String, dynamic> groupPeers = jsonDecode(await bind.mainLoadGroupSync());
@ -76,6 +76,7 @@ class _AutocompletePeerTileState extends State<AutocompletePeerTile>{
return list.sublist(0, n); return list.sublist(0, n);
} }
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final double _tileRadius = 5; final double _tileRadius = 5;
@ -86,8 +87,9 @@ class _AutocompletePeerTileState extends State<AutocompletePeerTile>{
color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6)); color: Theme.of(context).textTheme.titleLarge?.color?.withOpacity(0.6));
final child = GestureDetector( final child = GestureDetector(
onTap: () => widget.onSelect(), onTap: () => widget.onSelect(),
child: child: Padding(
Container( padding: EdgeInsets.only(left: 5, right: 5),
child: Container(
height: 42, height: 42,
margin: EdgeInsets.only(bottom: 5), margin: EdgeInsets.only(bottom: 5),
child: Row( child: Row(
@ -95,7 +97,8 @@ class _AutocompletePeerTileState extends State<AutocompletePeerTile>{
children: [ children: [
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: str2color('${widget.peer.id}${widget.peer.platform}', 0x7f), color: str2color(
'${widget.peer.id}${widget.peer.platform}', 0x7f),
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: Radius.circular(_tileRadius), topLeft: Radius.circular(_tileRadius),
bottomLeft: Radius.circular(_tileRadius), bottomLeft: Radius.circular(_tileRadius),
@ -106,9 +109,8 @@ class _AutocompletePeerTileState extends State<AutocompletePeerTile>{
height: null, height: null,
child: Padding( child: Padding(
padding: EdgeInsets.all(6), padding: EdgeInsets.all(6),
child: getPlatformImage(widget.peer.platform, size: 30) child: getPlatformImage(widget.peer.platform,
) size: 30))),
),
Expanded( Expanded(
child: Container( child: Container(
padding: EdgeInsets.only(left: 10), padding: EdgeInsets.only(left: 10),
@ -129,24 +131,37 @@ class _AutocompletePeerTileState extends State<AutocompletePeerTile>{
child: Column( child: Column(
children: [ children: [
Container( Container(
margin: EdgeInsets.only(top: 2), margin:
EdgeInsets.only(top: 2),
child: Row(children: [ child: Row(children: [
getOnline(8, widget.peer.online), getOnline(
8, widget.peer.online),
Expanded( Expanded(
child: Text( child: Text(
widget.peer.alias.isEmpty ? formatID(widget.peer.id) : widget.peer.alias, widget.peer.alias.isEmpty
overflow: TextOverflow.ellipsis, ? formatID(
style: Theme.of(context).textTheme.titleSmall, widget.peer.id)
: widget.peer.alias,
overflow:
TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.titleSmall,
)), )),
!widget.peer.alias.isEmpty? !widget.peer.alias.isEmpty
Padding( ? Padding(
padding: const EdgeInsets.only(left: 5, right: 5), padding:
const EdgeInsets
.only(
left: 5,
right: 5),
child: Text( child: Text(
"(${widget.peer.id})", "(${widget.peer.id})",
style: greyStyle, style: greyStyle,
overflow: TextOverflow.ellipsis, overflow:
) TextOverflow
) .ellipsis,
))
: Container(), : Container(),
])), ])),
Align( Align(
@ -155,20 +170,20 @@ class _AutocompletePeerTileState extends State<AutocompletePeerTile>{
name, name,
style: greyStyle, style: greyStyle,
textAlign: TextAlign.start, textAlign: TextAlign.start,
overflow: TextOverflow.ellipsis, overflow:
TextOverflow.ellipsis,
), ),
), ),
], ],
) )))),
))),
], ],
) )),
),
) )
], ],
))); ))));
final colors = final colors = _frontN(widget.peer.tags, 25)
_frontN(widget.peer.tags, 25).map((e) => gFFI.abModel.getTagColor(e)).toList(); .map((e) => gFFI.abModel.getTagColor(e))
.toList();
return Tooltip( return Tooltip(
message: isMobile message: isMobile
? '' ? ''

View File

@ -308,10 +308,27 @@ class _ConnectionPageState extends State<ConnectionPage>
AutocompleteOnSelected<Peer> onSelected, AutocompleteOnSelected<Peer> onSelected,
Iterable<Peer> options) { Iterable<Peer> options) {
double maxHeight = options.length * 50; double maxHeight = options.length * 50;
maxHeight = maxHeight > 200 ? 200 : maxHeight; if (options.length == 1) {
maxHeight = 52;
} else if (options.length == 3) {
maxHeight = 146;
} else if (options.length == 4) {
maxHeight = 193;
}
maxHeight = maxHeight.clamp(0, 200);
return Align( return Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 5,
spreadRadius: 1,
),
],
),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(5), borderRadius: BorderRadius.circular(5),
child: Material( child: Material(
@ -330,10 +347,12 @@ class _ConnectionPageState extends State<ConnectionPage>
), ),
)) ))
: Padding( : Padding(
padding: const EdgeInsets.only(top: 5), padding:
const EdgeInsets.only(top: 5),
child: ListView( child: ListView(
children: options children: options
.map((peer) => AutocompletePeerTile( .map((peer) =>
AutocompletePeerTile(
onSelect: () => onSelect: () =>
onSelected(peer), onSelected(peer),
peer: peer)) peer: peer))
@ -341,7 +360,7 @@ class _ConnectionPageState extends State<ConnectionPage>
), ),
), ),
), ),
)), ))),
); );
}, },
)), )),

View File

@ -52,6 +52,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
return list.sublist(0, n); return list.sublist(0, n);
} }
} }
bool isPeersLoading = false; bool isPeersLoading = false;
bool isPeersLoaded = false; bool isPeersLoaded = false;
StreamSubscription? _uniLinksSubscription; StreamSubscription? _uniLinksSubscription;
@ -163,8 +164,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
optionsBuilder: (TextEditingValue textEditingValue) { optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') { if (textEditingValue.text == '') {
return const Iterable<Peer>.empty(); return const Iterable<Peer>.empty();
} } else if (peers.isEmpty && !isPeersLoaded) {
else if (peers.isEmpty && !isPeersLoaded) {
Peer emptyPeer = Peer( Peer emptyPeer = Peer(
id: '', id: '',
username: '', username: '',
@ -179,9 +179,9 @@ class _ConnectionPageState extends State<ConnectionPage> {
loginName: '', loginName: '',
); );
return [emptyPeer]; return [emptyPeer];
} } else {
else { String textWithoutSpaces =
String textWithoutSpaces = textEditingValue.text.replaceAll(" ", ""); textEditingValue.text.replaceAll(" ", "");
if (int.tryParse(textWithoutSpaces) != null) { if (int.tryParse(textWithoutSpaces) != null) {
textEditingValue = TextEditingValue( textEditingValue = TextEditingValue(
text: textWithoutSpaces, text: textWithoutSpaces,
@ -190,27 +190,36 @@ class _ConnectionPageState extends State<ConnectionPage> {
} }
String textToFind = textEditingValue.text.toLowerCase(); String textToFind = textEditingValue.text.toLowerCase();
return peers.where((peer) => return peers
.where((peer) =>
peer.id.toLowerCase().contains(textToFind) || peer.id.toLowerCase().contains(textToFind) ||
peer.username.toLowerCase().contains(textToFind) || peer.username
peer.hostname.toLowerCase().contains(textToFind) || .toLowerCase()
.contains(textToFind) ||
peer.hostname
.toLowerCase()
.contains(textToFind) ||
peer.alias.toLowerCase().contains(textToFind)) peer.alias.toLowerCase().contains(textToFind))
.toList(); .toList();
} }
}, },
fieldViewBuilder: (BuildContext context, fieldViewBuilder: (BuildContext context,
TextEditingController fieldTextEditingController, TextEditingController fieldTextEditingController,
FocusNode fieldFocusNode, VoidCallback onFieldSubmitted) { FocusNode fieldFocusNode,
VoidCallback onFieldSubmitted) {
fieldTextEditingController.text = _idController.text; fieldTextEditingController.text = _idController.text;
fieldFocusNode.addListener(() async { fieldFocusNode.addListener(() async {
_idEmpty.value = fieldTextEditingController.text.isEmpty; _idEmpty.value =
fieldTextEditingController.text.isEmpty;
if (fieldFocusNode.hasFocus && !isPeersLoading) { if (fieldFocusNode.hasFocus && !isPeersLoading) {
_fetchPeers(); _fetchPeers();
} }
}); });
final textLength = fieldTextEditingController.value.text.length; final textLength =
fieldTextEditingController.value.text.length;
// select all to facilitate removing text, just following the behavior of address input of chrome // select all to facilitate removing text, just following the behavior of address input of chrome
fieldTextEditingController.selection = TextSelection(baseOffset: 0, extentOffset: textLength); fieldTextEditingController.selection = TextSelection(
baseOffset: 0, extentOffset: textLength);
return AutoSizeTextField( return AutoSizeTextField(
controller: fieldTextEditingController, controller: fieldTextEditingController,
focusNode: fieldFocusNode, focusNode: fieldFocusNode,
@ -253,11 +262,30 @@ class _ConnectionPageState extends State<ConnectionPage> {
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
}); });
}, },
optionsViewBuilder: (BuildContext context, AutocompleteOnSelected<Peer> onSelected, Iterable<Peer> options) { optionsViewBuilder: (BuildContext context,
AutocompleteOnSelected<Peer> onSelected,
Iterable<Peer> options) {
double maxHeight = options.length * 50; double maxHeight = options.length * 50;
maxHeight = maxHeight > 200 ? 200 : maxHeight; if (options.length == 1) {
maxHeight = 52;
} else if (options.length == 3) {
maxHeight = 146;
} else if (options.length == 4) {
maxHeight = 193;
}
maxHeight = maxHeight.clamp(0, 200);
return Align( return Align(
alignment: Alignment.topLeft, alignment: Alignment.topLeft,
child: Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.3),
blurRadius: 5,
spreadRadius: 1,
),
],
),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(5), borderRadius: BorderRadius.circular(5),
child: Material( child: Material(
@ -271,14 +299,22 @@ class _ConnectionPageState extends State<ConnectionPage> {
? Container( ? Container(
height: 80, height: 80,
child: Center( child: Center(
child: CircularProgressIndicator( child:
CircularProgressIndicator(
strokeWidth: 2, strokeWidth: 2,
))) )))
: ListView( : ListView(
padding: EdgeInsets.only(top: 5), padding:
children: options.map((peer) => AutocompletePeerTile(onSelect: () => onSelected(peer), peer: peer)).toList(), EdgeInsets.only(top: 5),
)))) children: options
); .map((peer) =>
AutocompletePeerTile(
onSelect: () =>
onSelected(
peer),
peer: peer))
.toList(),
))))));
}, },
), ),
), ),