mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-11-04 12:14:11 +00:00 
			
		
		
		
	Created inline user picker
This commit is contained in:
		
							
								
								
									
										31
									
								
								lib/helpers/search_helper.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								lib/helpers/search_helper.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					import 'package:comunic/helpers/users_helper.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/lists/users_list.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/models/api_request.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/utils/list_utils.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Search helper
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SearchHelper {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Search for user. This method returns information about the target users
 | 
				
			||||||
 | 
					  ///
 | 
				
			||||||
 | 
					  /// Returns information about the target users or null if an error occurred
 | 
				
			||||||
 | 
					  Future<UsersList> searchUser(String query) async {
 | 
				
			||||||
 | 
					    // Execute the query on the server
 | 
				
			||||||
 | 
					    final response = await APIRequest(
 | 
				
			||||||
 | 
					        uri: "user/search",
 | 
				
			||||||
 | 
					        needLogin: true,
 | 
				
			||||||
 | 
					        args: {
 | 
				
			||||||
 | 
					          "query": query
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    ).exec();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (response.code != 200) return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return await UsersHelper().getUsersInfo(
 | 
				
			||||||
 | 
					        listToIntList(response.getArray()));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										31
									
								
								lib/ui/tiles/simple_user_tile.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								lib/ui/tiles/simple_user_tile.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					import 'package:comunic/models/user.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/ui/widgets/account_image_widget.dart';
 | 
				
			||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Simple user tile
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Basically this only shows the name of the user + its account image
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef OnUserTap = void Function(User);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SimpleUserTile extends StatelessWidget {
 | 
				
			||||||
 | 
					  final User user;
 | 
				
			||||||
 | 
					  final OnUserTap onTap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const SimpleUserTile({Key key, this.user, this.onTap})
 | 
				
			||||||
 | 
					      : assert(user != null),
 | 
				
			||||||
 | 
					        super(key: key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    return ListTile(
 | 
				
			||||||
 | 
					      onTap: onTap == null ? null : () => onTap(user),
 | 
				
			||||||
 | 
					      leading: AccountImageWidget(
 | 
				
			||||||
 | 
					        user: user,
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      title: Text(user.fullName),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										117
									
								
								lib/ui/widgets/pick_user_widget.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								lib/ui/widgets/pick_user_widget.dart
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
				
			|||||||
 | 
					import 'package:comunic/helpers/search_helper.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/lists/users_list.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/models/user.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/ui/tiles/simple_user_tile.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/utils/intl_utils.dart';
 | 
				
			||||||
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Pick user widget
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// Allows to choose a user by starting to type his name and tapping on it
 | 
				
			||||||
 | 
					/// when his name appear
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// @author Pierre HUBERT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef OnSelectUserCallback = void Function(User);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PickUserWidget extends StatefulWidget {
 | 
				
			||||||
 | 
					  final OnSelectUserCallback onSelectUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const PickUserWidget({Key key, @required this.onSelectUser})
 | 
				
			||||||
 | 
					      : assert(onSelectUser != null),
 | 
				
			||||||
 | 
					        super(key: key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  State<StatefulWidget> createState() => _PickUserWidgetState();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class _PickUserWidgetState extends State<PickUserWidget> {
 | 
				
			||||||
 | 
					  // Helper
 | 
				
			||||||
 | 
					  final SearchHelper _searchHelper = SearchHelper();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Widget properties
 | 
				
			||||||
 | 
					  final FocusNode _focusNode = FocusNode();
 | 
				
			||||||
 | 
					  final TextEditingController _controller = TextEditingController();
 | 
				
			||||||
 | 
					  OverlayEntry _overlayEntry;
 | 
				
			||||||
 | 
					  UsersList _suggestions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void initState() {
 | 
				
			||||||
 | 
					    super.initState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _focusNode.addListener(() {
 | 
				
			||||||
 | 
					      if (_focusNode.hasFocus) {
 | 
				
			||||||
 | 
					        //Check for focus
 | 
				
			||||||
 | 
					        _overlayEntry = _createOverlayEntry();
 | 
				
			||||||
 | 
					        Overlay.of(context).insert(_overlayEntry);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        //Remove overlay
 | 
				
			||||||
 | 
					        _removeOverlay();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
 | 
					    return TextField(
 | 
				
			||||||
 | 
					      focusNode: _focusNode,
 | 
				
			||||||
 | 
					      onChanged: (s) => _updateSuggestions(),
 | 
				
			||||||
 | 
					      controller: _controller,
 | 
				
			||||||
 | 
					      decoration: InputDecoration(labelText: tr("Select user")),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  OverlayEntry _createOverlayEntry() {
 | 
				
			||||||
 | 
					    RenderBox renderBox = context.findRenderObject();
 | 
				
			||||||
 | 
					    final size = renderBox.size;
 | 
				
			||||||
 | 
					    final offset = renderBox.localToGlobal(Offset.zero);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return OverlayEntry(builder: (c) {
 | 
				
			||||||
 | 
					      return Positioned(
 | 
				
			||||||
 | 
					        left: offset.dx,
 | 
				
			||||||
 | 
					        top: offset.dy + size.height + 5.0,
 | 
				
			||||||
 | 
					        width: size.width,
 | 
				
			||||||
 | 
					        child: Material(
 | 
				
			||||||
 | 
					          elevation: 4.0,
 | 
				
			||||||
 | 
					          child: ListView.builder(
 | 
				
			||||||
 | 
					            padding: EdgeInsets.zero,
 | 
				
			||||||
 | 
					            shrinkWrap: true,
 | 
				
			||||||
 | 
					            itemCount: _suggestions == null ? 0 : _suggestions.length,
 | 
				
			||||||
 | 
					            itemBuilder: (c, i) => SimpleUserTile(
 | 
				
			||||||
 | 
					                  user: _suggestions[i],
 | 
				
			||||||
 | 
					                  onTap: _userTapped,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void _removeOverlay() {
 | 
				
			||||||
 | 
					    if (_overlayEntry != null) {
 | 
				
			||||||
 | 
					      _overlayEntry.remove();
 | 
				
			||||||
 | 
					      _overlayEntry = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// This method get called each time the input value is updated
 | 
				
			||||||
 | 
					  Future<void> _updateSuggestions() async {
 | 
				
			||||||
 | 
					    if (_controller.value.text.length == 0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final results = await _searchHelper.searchUser(_controller.value.text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (results == null) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _suggestions = results;
 | 
				
			||||||
 | 
					    if (_overlayEntry != null) _overlayEntry.markNeedsBuild();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /// Method called each time a user is tapped (selected)
 | 
				
			||||||
 | 
					  void _userTapped(User user) {
 | 
				
			||||||
 | 
					    _controller.text = user.fullName;
 | 
				
			||||||
 | 
					    _removeOverlay();
 | 
				
			||||||
 | 
					    _focusNode.unfocus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    widget.onSelectUser(user);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user