diff --git a/packages/neon/neon/lib/src/widgets/autocomplete.dart b/packages/neon/neon/lib/src/widgets/autocomplete.dart new file mode 100644 index 00000000..06ea8249 --- /dev/null +++ b/packages/neon/neon/lib/src/widgets/autocomplete.dart @@ -0,0 +1,101 @@ +import 'package:flutter/material.dart'; +import 'package:neon/src/blocs/accounts.dart'; +import 'package:neon/src/models/account.dart'; +import 'package:neon/src/widgets/group_avatar.dart'; +import 'package:neon/src/widgets/user_avatar.dart'; +import 'package:nextcloud/core.dart' as core; +import 'package:provider/provider.dart'; + +class NeonAutocomplete extends StatelessWidget { + const NeonAutocomplete({ + required this.account, + required this.itemType, + required this.itemId, + required this.shareTypes, + required this.onSelected, + this.sorter, + this.limit = 10, + this.validator, + this.decoration, + this.onFieldSubmitted, + super.key, + }); + + final Account account; + + final String itemType; + final String itemId; + final List shareTypes; + final void Function(core.AutocompleteResult entry) onSelected; + final String? sorter; + final int limit; + final FormFieldValidator? validator; + final InputDecoration? decoration; + final ValueChanged? onFieldSubmitted; + + @override + Widget build(final BuildContext context) => Autocomplete( + fieldViewBuilder: ( + final context, + final controller, + final focusNode, + final onFieldSubmitted, + ) => + TextFormField( + controller: controller, + focusNode: focusNode, + validator: validator, + decoration: decoration, + onFieldSubmitted: (final value) { + onFieldSubmitted(); + this.onFieldSubmitted?.call(value); + }, + ), + optionsBuilder: (final text) async { + final result = await account.client.core.autoComplete.$get( + search: text.text, + itemType: itemType, + itemId: itemId, + shareTypes: shareTypes, + ); + return result.body.ocs.data; + }, + displayStringForOption: (final option) => option.id, + optionsViewBuilder: (final context, final onSelected, final options) => Align( + alignment: Alignment.topLeft, + child: Material( + elevation: 4, + child: ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 200), + child: ListView.builder( + padding: EdgeInsets.zero, + shrinkWrap: true, + itemCount: options.length, + itemBuilder: (final context, final index) { + final option = options.elementAt(index); + Widget? icon; + switch (option.source) { + case 'users': + icon = NeonUserAvatar( + username: option.id, + account: Provider.of(context, listen: false).activeAccount.value!, + ); + case 'groups': + icon = const NeonGroupAvatar(); + } + return ListTile( + title: Text(option.label), + subtitle: Text(option.id), + leading: icon, + onTap: () { + onSelected(option); + }, + ); + }, + ), + ), + ), + ), + onSelected: onSelected, + ); +} diff --git a/packages/neon/neon/lib/src/widgets/group_avatar.dart b/packages/neon/neon/lib/src/widgets/group_avatar.dart new file mode 100644 index 00000000..6034381b --- /dev/null +++ b/packages/neon/neon/lib/src/widgets/group_avatar.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:neon/src/theme/sizes.dart'; + +class NeonGroupAvatar extends StatelessWidget { + const NeonGroupAvatar({ + this.icon = Icons.group, + this.backgroundColor, + this.foregroundColor, + super.key, + }); + + final IconData icon; + final Color? backgroundColor; + final Color? foregroundColor; + + @override + Widget build(final BuildContext context) => CircleAvatar( + radius: smallIconSize, + backgroundColor: backgroundColor, + child: Icon( + icon, + color: foregroundColor, + size: smallIconSize, + ), + ); +} diff --git a/packages/neon/neon/lib/widgets.dart b/packages/neon/neon/lib/widgets.dart index 7504b54b..94db971f 100644 --- a/packages/neon/neon/lib/widgets.dart +++ b/packages/neon/neon/lib/widgets.dart @@ -1,5 +1,7 @@ +export 'package:neon/src/widgets/autocomplete.dart'; export 'package:neon/src/widgets/dialog.dart'; export 'package:neon/src/widgets/error.dart'; +export 'package:neon/src/widgets/group_avatar.dart'; export 'package:neon/src/widgets/image.dart'; export 'package:neon/src/widgets/linear_progress_indicator.dart'; export 'package:neon/src/widgets/list_view.dart'; diff --git a/packages/neon/neon/pubspec.yaml b/packages/neon/neon/pubspec.yaml index a695b252..a77539c2 100644 --- a/packages/neon/neon/pubspec.yaml +++ b/packages/neon/neon/pubspec.yaml @@ -22,7 +22,7 @@ dependencies: sdk: flutter flutter_material_design_icons: ^1.0.0 flutter_native_splash: ^2.0.0 - flutter_parsed_text: ^2.0.0 + flutter_parsed_text: ^2.1.0 flutter_svg: ^2.0.0 flutter_zxing: ^1.0.0 go_router: ^12.0.0