1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-11-25 22:39:22 +00:00
comunicmobile/lib/ui/widgets/tablet_mode/global_search_field.dart

191 lines
5.2 KiB
Dart
Raw Normal View History

2020-05-08 19:02:39 +00:00
import 'package:comunic/helpers/groups_helper.dart';
import 'package:comunic/helpers/search_helper.dart';
import 'package:comunic/helpers/users_helper.dart';
import 'package:comunic/lists/groups_list.dart';
import 'package:comunic/lists/search_results_list.dart';
import 'package:comunic/lists/users_list.dart';
import 'package:comunic/models/search_result.dart';
2020-05-11 16:39:07 +00:00
import 'package:comunic/ui/routes/main_route/main_route.dart';
2020-05-08 19:02:39 +00:00
import 'package:comunic/ui/widgets/account_image_widget.dart';
import 'package:comunic/ui/widgets/group_icon_widget.dart';
2020-05-08 14:06:10 +00:00
import 'package:comunic/utils/intl_utils.dart';
2020-05-08 19:02:39 +00:00
import 'package:comunic/utils/ui_utils.dart';
2020-05-08 14:06:10 +00:00
import 'package:flutter/material.dart';
/// Global search field
///
/// @author Pierre Hubert
const _MainSearchColor = Color(0xFF999990);
2020-05-08 19:02:39 +00:00
class _SearchResults {
final SearchResultsList list;
final UsersList users;
final GroupsList groups;
_SearchResults(this.list, this.users, this.groups);
}
2020-05-08 14:06:10 +00:00
class GlobalSearchField extends StatefulWidget {
@override
_GlobalSearchFieldState createState() => _GlobalSearchFieldState();
}
class _GlobalSearchFieldState extends State<GlobalSearchField> {
2020-05-08 19:02:39 +00:00
final _focusNode = FocusNode();
final _controller = TextEditingController();
_SearchResults _searchResultsList;
OverlayEntry _overlayEntry;
@override
void initState() {
super.initState();
_focusNode.addListener(() {
if (!_focusNode.hasFocus) _removeOverlay();
});
}
2020-05-08 14:06:10 +00:00
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Material(
color: Color(0xFF374850),
borderRadius: BorderRadius.all(Radius.circular(5.0)),
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: TextField(
2020-05-08 19:07:51 +00:00
autofocus: false,
2020-05-08 19:02:39 +00:00
controller: _controller,
focusNode: _focusNode,
2020-05-08 14:06:10 +00:00
textAlignVertical: TextAlignVertical.center,
2020-05-08 19:02:39 +00:00
onChanged: (s) => _refreshSearch(),
2020-05-08 14:06:10 +00:00
decoration: InputDecoration(
hintText: tr("Search..."),
hintStyle: TextStyle(color: _MainSearchColor),
suffixIcon: Icon(
Icons.search,
color: _MainSearchColor,
),
focusedBorder: InputBorder.none,
border: InputBorder.none,
alignLabelWithHint: false,
),
),
),
),
);
}
2020-05-08 19:02:39 +00:00
void _refreshSearch() async {
if (_controller.text.length < 1) {
_removeOverlay();
return;
}
_showOverlay();
try {
final results = await SearchHelper().globalSearch(_controller.text);
final users = await UsersHelper().getListWithThrow(results.usersId);
final groups = await GroupsHelper().getListOrThrow(results.groupsId);
_searchResultsList = _SearchResults(results, users, groups);
if (_overlayEntry != null) _overlayEntry.markNeedsBuild();
} catch (e, s) {
print("Could not perform search! $e\n$s");
showSimpleSnack(context, tr("Could not perform search!"));
}
}
void _showOverlay() {
if (_overlayEntry == null) {
_overlayEntry = _createOverlayEntry();
Overlay.of(context).insert(_overlayEntry);
}
}
void _removeOverlay() {
if (_overlayEntry != null) {
_overlayEntry.remove();
_overlayEntry = null;
}
}
OverlayEntry _createOverlayEntry() {
RenderBox renderBox = context.findRenderObject();
var size = renderBox.size;
var offset = renderBox.localToGlobal(Offset.zero);
return OverlayEntry(
builder: (context) => Positioned(
left: offset.dx,
top: offset.dy + size.height + 5.0,
width: size.width,
height: 300,
child: Material(
elevation: 4.0,
2020-05-11 16:39:07 +00:00
child: _SearchResultsWidget(
results: _searchResultsList,
onTap: _removeOverlay,
),
2020-05-08 19:02:39 +00:00
),
));
}
}
class _SearchResultsWidget extends StatelessWidget {
final _SearchResults results;
2020-05-11 16:39:07 +00:00
final Function() onTap;
2020-05-08 19:02:39 +00:00
2020-05-11 16:39:07 +00:00
const _SearchResultsWidget({
Key key,
@required this.results,
@required this.onTap,
}) : assert(onTap != null),
super(key: key);
2020-05-08 19:02:39 +00:00
@override
Widget build(BuildContext context) {
if (results == null) return Container();
return ListView.builder(
itemBuilder: _builder,
itemCount: results.list.length,
);
}
Widget _builder(BuildContext context, int index) {
final res = results.list[index];
switch (res.kind) {
case SearchResultKind.USER:
final user = results.users.getUser(res.id);
return ListTile(
leading: AccountImageWidget(user: user),
title: Text(user.displayName),
2020-05-11 16:39:07 +00:00
onTap: () {
MainController.of(context).openUserPage(user.id);
onTap();
},
2020-05-08 19:02:39 +00:00
);
case SearchResultKind.GROUP:
final group = results.groups.getGroup(res.id);
return ListTile(
leading: GroupIcon(group: group),
title: Text(group.displayName),
2020-05-11 16:39:07 +00:00
onTap: () {
MainController.of(context).openGroup(group.id);
onTap();
},
2020-05-08 19:02:39 +00:00
);
}
throw Exception("Unreachable statement!");
}
2020-05-08 14:06:10 +00:00
}