mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:29:32 -05:00 
			
		
		
		
	deps: update dependency auto_route to v8 Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
		
			
				
	
	
		
			562 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			562 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
import 'dart:async';
 | 
						|
 | 
						|
import 'package:auto_route/auto_route.dart';
 | 
						|
import 'package:flutter/material.dart';
 | 
						|
import 'package:flutter_hooks/flutter_hooks.dart';
 | 
						|
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
						|
import 'package:immich_mobile/entities/asset.entity.dart';
 | 
						|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
 | 
						|
import 'package:immich_mobile/models/search/search_filter.model.dart';
 | 
						|
import 'package:immich_mobile/providers/search/paginated_search.provider.dart';
 | 
						|
import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart';
 | 
						|
import 'package:immich_mobile/widgets/search/search_filter/camera_picker.dart';
 | 
						|
import 'package:immich_mobile/widgets/search/search_filter/display_option_picker.dart';
 | 
						|
import 'package:immich_mobile/widgets/search/search_filter/filter_bottom_sheet_scaffold.dart';
 | 
						|
import 'package:immich_mobile/widgets/search/search_filter/location_picker.dart';
 | 
						|
import 'package:immich_mobile/widgets/search/search_filter/media_type_picker.dart';
 | 
						|
import 'package:immich_mobile/widgets/search/search_filter/people_picker.dart';
 | 
						|
import 'package:immich_mobile/widgets/search/search_filter/search_filter_chip.dart';
 | 
						|
import 'package:immich_mobile/widgets/search/search_filter/search_filter_utils.dart';
 | 
						|
import 'package:openapi/api.dart';
 | 
						|
 | 
						|
@RoutePage()
 | 
						|
class SearchInputPage extends HookConsumerWidget {
 | 
						|
  const SearchInputPage({super.key, this.prefilter});
 | 
						|
 | 
						|
  final SearchFilter? prefilter;
 | 
						|
 | 
						|
  @override
 | 
						|
  Widget build(BuildContext context, WidgetRef ref) {
 | 
						|
    final isContextualSearch = useState(true);
 | 
						|
    final textSearchController = useTextEditingController();
 | 
						|
    final filter = useState<SearchFilter>(
 | 
						|
      SearchFilter(
 | 
						|
        people: prefilter?.people ?? {},
 | 
						|
        location: prefilter?.location ?? SearchLocationFilter(),
 | 
						|
        camera: prefilter?.camera ?? SearchCameraFilter(),
 | 
						|
        date: prefilter?.date ?? SearchDateFilter(),
 | 
						|
        display: prefilter?.display ??
 | 
						|
            SearchDisplayFilters(
 | 
						|
              isNotInAlbum: false,
 | 
						|
              isArchive: false,
 | 
						|
              isFavorite: false,
 | 
						|
            ),
 | 
						|
        mediaType: prefilter?.mediaType ?? AssetType.other,
 | 
						|
      ),
 | 
						|
    );
 | 
						|
 | 
						|
    final previousFilter = useState(filter.value);
 | 
						|
 | 
						|
    final peopleCurrentFilterWidget = useState<Widget?>(null);
 | 
						|
    final dateRangeCurrentFilterWidget = useState<Widget?>(null);
 | 
						|
    final cameraCurrentFilterWidget = useState<Widget?>(null);
 | 
						|
    final locationCurrentFilterWidget = useState<Widget?>(null);
 | 
						|
    final mediaTypeCurrentFilterWidget = useState<Widget?>(null);
 | 
						|
    final displayOptionCurrentFilterWidget = useState<Widget?>(null);
 | 
						|
 | 
						|
    final currentPage = useState(1);
 | 
						|
    final searchProvider = ref.watch(paginatedSearchProvider);
 | 
						|
    final searchResultCount = useState(0);
 | 
						|
 | 
						|
    search() async {
 | 
						|
      if (prefilter == null && filter.value == previousFilter.value) return;
 | 
						|
 | 
						|
      ref.watch(paginatedSearchProvider.notifier).clear();
 | 
						|
 | 
						|
      currentPage.value = 1;
 | 
						|
 | 
						|
      final searchResult = await ref
 | 
						|
          .watch(paginatedSearchProvider.notifier)
 | 
						|
          .getNextPage(filter.value, currentPage.value);
 | 
						|
      previousFilter.value = filter.value;
 | 
						|
 | 
						|
      searchResultCount.value = searchResult.length;
 | 
						|
    }
 | 
						|
 | 
						|
    searchPrefilter() {
 | 
						|
      if (prefilter != null) {
 | 
						|
        Future.delayed(
 | 
						|
          Duration.zero,
 | 
						|
          () {
 | 
						|
            search();
 | 
						|
 | 
						|
            if (prefilter!.location.city != null) {
 | 
						|
              locationCurrentFilterWidget.value = Text(
 | 
						|
                prefilter!.location.city!,
 | 
						|
                style: context.textTheme.labelLarge,
 | 
						|
              );
 | 
						|
            }
 | 
						|
          },
 | 
						|
        );
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    useEffect(
 | 
						|
      () {
 | 
						|
        searchPrefilter();
 | 
						|
        return null;
 | 
						|
      },
 | 
						|
      [],
 | 
						|
    );
 | 
						|
 | 
						|
    loadMoreSearchResult() async {
 | 
						|
      currentPage.value += 1;
 | 
						|
      final searchResult = await ref
 | 
						|
          .watch(paginatedSearchProvider.notifier)
 | 
						|
          .getNextPage(filter.value, currentPage.value);
 | 
						|
      searchResultCount.value = searchResult.length;
 | 
						|
    }
 | 
						|
 | 
						|
    showPeoplePicker() {
 | 
						|
      handleOnSelect(Set<PersonResponseDto> value) {
 | 
						|
        filter.value = filter.value.copyWith(
 | 
						|
          people: value,
 | 
						|
        );
 | 
						|
 | 
						|
        peopleCurrentFilterWidget.value = Text(
 | 
						|
          value.map((e) => e.name != '' ? e.name : "No name").join(', '),
 | 
						|
          style: context.textTheme.labelLarge,
 | 
						|
        );
 | 
						|
      }
 | 
						|
 | 
						|
      handleClear() {
 | 
						|
        filter.value = filter.value.copyWith(
 | 
						|
          people: {},
 | 
						|
        );
 | 
						|
 | 
						|
        peopleCurrentFilterWidget.value = null;
 | 
						|
        search();
 | 
						|
      }
 | 
						|
 | 
						|
      showFilterBottomSheet(
 | 
						|
        context: context,
 | 
						|
        isScrollControlled: true,
 | 
						|
        child: FractionallySizedBox(
 | 
						|
          heightFactor: 0.8,
 | 
						|
          child: FilterBottomSheetScaffold(
 | 
						|
            title: 'Select people',
 | 
						|
            expanded: true,
 | 
						|
            onSearch: search,
 | 
						|
            onClear: handleClear,
 | 
						|
            child: PeoplePicker(
 | 
						|
              onSelect: handleOnSelect,
 | 
						|
              filter: filter.value.people,
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
        ),
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    showLocationPicker() {
 | 
						|
      handleOnSelect(Map<String, String?> value) {
 | 
						|
        filter.value = filter.value.copyWith(
 | 
						|
          location: SearchLocationFilter(
 | 
						|
            country: value['country'],
 | 
						|
            city: value['city'],
 | 
						|
            state: value['state'],
 | 
						|
          ),
 | 
						|
        );
 | 
						|
 | 
						|
        final locationText = <String>[];
 | 
						|
        if (value['country'] != null) {
 | 
						|
          locationText.add(value['country']!);
 | 
						|
        }
 | 
						|
 | 
						|
        if (value['state'] != null) {
 | 
						|
          locationText.add(value['state']!);
 | 
						|
        }
 | 
						|
 | 
						|
        if (value['city'] != null) {
 | 
						|
          locationText.add(value['city']!);
 | 
						|
        }
 | 
						|
 | 
						|
        locationCurrentFilterWidget.value = Text(
 | 
						|
          locationText.join(', '),
 | 
						|
          style: context.textTheme.labelLarge,
 | 
						|
        );
 | 
						|
      }
 | 
						|
 | 
						|
      handleClear() {
 | 
						|
        filter.value = filter.value.copyWith(
 | 
						|
          location: SearchLocationFilter(),
 | 
						|
        );
 | 
						|
 | 
						|
        locationCurrentFilterWidget.value = null;
 | 
						|
        search();
 | 
						|
      }
 | 
						|
 | 
						|
      showFilterBottomSheet(
 | 
						|
        context: context,
 | 
						|
        isScrollControlled: true,
 | 
						|
        isDismissible: false,
 | 
						|
        child: FilterBottomSheetScaffold(
 | 
						|
          title: 'Select location',
 | 
						|
          onSearch: search,
 | 
						|
          onClear: handleClear,
 | 
						|
          child: Padding(
 | 
						|
            padding: const EdgeInsets.symmetric(vertical: 16.0),
 | 
						|
            child: Container(
 | 
						|
              padding: EdgeInsets.only(
 | 
						|
                bottom: MediaQuery.of(context).viewInsets.bottom,
 | 
						|
              ),
 | 
						|
              child: LocationPicker(
 | 
						|
                onSelected: handleOnSelect,
 | 
						|
                filter: filter.value.location,
 | 
						|
              ),
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
        ),
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    showCameraPicker() {
 | 
						|
      handleOnSelect(Map<String, String?> value) {
 | 
						|
        filter.value = filter.value.copyWith(
 | 
						|
          camera: SearchCameraFilter(
 | 
						|
            make: value['make'],
 | 
						|
            model: value['model'],
 | 
						|
          ),
 | 
						|
        );
 | 
						|
 | 
						|
        cameraCurrentFilterWidget.value = Text(
 | 
						|
          '${value['make'] ?? ''} ${value['model'] ?? ''}',
 | 
						|
          style: context.textTheme.labelLarge,
 | 
						|
        );
 | 
						|
      }
 | 
						|
 | 
						|
      handleClear() {
 | 
						|
        filter.value = filter.value.copyWith(
 | 
						|
          camera: SearchCameraFilter(),
 | 
						|
        );
 | 
						|
 | 
						|
        cameraCurrentFilterWidget.value = null;
 | 
						|
        search();
 | 
						|
      }
 | 
						|
 | 
						|
      showFilterBottomSheet(
 | 
						|
        context: context,
 | 
						|
        isScrollControlled: true,
 | 
						|
        isDismissible: false,
 | 
						|
        child: FilterBottomSheetScaffold(
 | 
						|
          title: 'Select camera type',
 | 
						|
          onSearch: search,
 | 
						|
          onClear: handleClear,
 | 
						|
          child: Padding(
 | 
						|
            padding: const EdgeInsets.symmetric(vertical: 16.0),
 | 
						|
            child: CameraPicker(
 | 
						|
              onSelect: handleOnSelect,
 | 
						|
              filter: filter.value.camera,
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
        ),
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    showDatePicker() async {
 | 
						|
      final firstDate = DateTime(1900);
 | 
						|
      final lastDate = DateTime.now();
 | 
						|
 | 
						|
      final date = await showDateRangePicker(
 | 
						|
        context: context,
 | 
						|
        firstDate: firstDate,
 | 
						|
        lastDate: lastDate,
 | 
						|
        currentDate: DateTime.now(),
 | 
						|
        initialDateRange: DateTimeRange(
 | 
						|
          start: filter.value.date.takenAfter ?? lastDate,
 | 
						|
          end: filter.value.date.takenBefore ?? lastDate,
 | 
						|
        ),
 | 
						|
        helpText: 'Select a date range',
 | 
						|
        cancelText: 'Cancel',
 | 
						|
        confirmText: 'Select',
 | 
						|
        saveText: 'Save',
 | 
						|
        errorFormatText: 'Invalid date format',
 | 
						|
        errorInvalidText: 'Invalid date',
 | 
						|
        fieldStartHintText: 'Start date',
 | 
						|
        fieldEndHintText: 'End date',
 | 
						|
        initialEntryMode: DatePickerEntryMode.input,
 | 
						|
      );
 | 
						|
 | 
						|
      if (date == null) {
 | 
						|
        filter.value = filter.value.copyWith(
 | 
						|
          date: SearchDateFilter(),
 | 
						|
        );
 | 
						|
 | 
						|
        dateRangeCurrentFilterWidget.value = null;
 | 
						|
        search();
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      filter.value = filter.value.copyWith(
 | 
						|
        date: SearchDateFilter(
 | 
						|
          takenAfter: date.start,
 | 
						|
          takenBefore: date.end.add(
 | 
						|
            const Duration(
 | 
						|
              hours: 23,
 | 
						|
              minutes: 59,
 | 
						|
              seconds: 59,
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
        ),
 | 
						|
      );
 | 
						|
 | 
						|
      // If date range is less than 24 hours, set the end date to the end of the day
 | 
						|
      if (date.end.difference(date.start).inHours < 24) {
 | 
						|
        dateRangeCurrentFilterWidget.value = Text(
 | 
						|
          date.start.toLocal().toIso8601String().split('T').first,
 | 
						|
          style: context.textTheme.labelLarge,
 | 
						|
        );
 | 
						|
      } else {
 | 
						|
        dateRangeCurrentFilterWidget.value = Text(
 | 
						|
          '${date.start.toLocal().toIso8601String().split('T').first} to ${date.end.toLocal().toIso8601String().split('T').first}',
 | 
						|
          style: context.textTheme.labelLarge,
 | 
						|
        );
 | 
						|
      }
 | 
						|
 | 
						|
      search();
 | 
						|
    }
 | 
						|
 | 
						|
    // MEDIA PICKER
 | 
						|
    showMediaTypePicker() {
 | 
						|
      handleOnSelected(AssetType assetType) {
 | 
						|
        filter.value = filter.value.copyWith(
 | 
						|
          mediaType: assetType,
 | 
						|
        );
 | 
						|
 | 
						|
        mediaTypeCurrentFilterWidget.value = Text(
 | 
						|
          assetType == AssetType.image ? 'Image' : 'Video',
 | 
						|
          style: context.textTheme.labelLarge,
 | 
						|
        );
 | 
						|
      }
 | 
						|
 | 
						|
      handleClear() {
 | 
						|
        filter.value = filter.value.copyWith(
 | 
						|
          mediaType: AssetType.other,
 | 
						|
        );
 | 
						|
 | 
						|
        mediaTypeCurrentFilterWidget.value = null;
 | 
						|
        search();
 | 
						|
      }
 | 
						|
 | 
						|
      showFilterBottomSheet(
 | 
						|
        context: context,
 | 
						|
        child: FilterBottomSheetScaffold(
 | 
						|
          title: 'Select media type',
 | 
						|
          onSearch: search,
 | 
						|
          onClear: handleClear,
 | 
						|
          child: MediaTypePicker(
 | 
						|
            onSelect: handleOnSelected,
 | 
						|
            filter: filter.value.mediaType,
 | 
						|
          ),
 | 
						|
        ),
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    // DISPLAY OPTION
 | 
						|
    showDisplayOptionPicker() {
 | 
						|
      handleOnSelect(Map<DisplayOption, bool> value) {
 | 
						|
        final filterText = <String>[];
 | 
						|
 | 
						|
        value.forEach((key, value) {
 | 
						|
          switch (key) {
 | 
						|
            case DisplayOption.notInAlbum:
 | 
						|
              filter.value = filter.value.copyWith(
 | 
						|
                display: filter.value.display.copyWith(
 | 
						|
                  isNotInAlbum: value,
 | 
						|
                ),
 | 
						|
              );
 | 
						|
              if (value) filterText.add('Not in album');
 | 
						|
              break;
 | 
						|
            case DisplayOption.archive:
 | 
						|
              filter.value = filter.value.copyWith(
 | 
						|
                display: filter.value.display.copyWith(
 | 
						|
                  isArchive: value,
 | 
						|
                ),
 | 
						|
              );
 | 
						|
              if (value) filterText.add('Archive');
 | 
						|
              break;
 | 
						|
            case DisplayOption.favorite:
 | 
						|
              filter.value = filter.value.copyWith(
 | 
						|
                display: filter.value.display.copyWith(
 | 
						|
                  isFavorite: value,
 | 
						|
                ),
 | 
						|
              );
 | 
						|
              if (value) filterText.add('Favorite');
 | 
						|
              break;
 | 
						|
          }
 | 
						|
        });
 | 
						|
 | 
						|
        displayOptionCurrentFilterWidget.value = Text(
 | 
						|
          filterText.join(', '),
 | 
						|
          style: context.textTheme.labelLarge,
 | 
						|
        );
 | 
						|
      }
 | 
						|
 | 
						|
      handleClear() {
 | 
						|
        filter.value = filter.value.copyWith(
 | 
						|
          display: SearchDisplayFilters(
 | 
						|
            isNotInAlbum: false,
 | 
						|
            isArchive: false,
 | 
						|
            isFavorite: false,
 | 
						|
          ),
 | 
						|
        );
 | 
						|
 | 
						|
        displayOptionCurrentFilterWidget.value = null;
 | 
						|
        search();
 | 
						|
      }
 | 
						|
 | 
						|
      showFilterBottomSheet(
 | 
						|
        context: context,
 | 
						|
        child: FilterBottomSheetScaffold(
 | 
						|
          title: 'Display options',
 | 
						|
          onSearch: search,
 | 
						|
          onClear: handleClear,
 | 
						|
          child: DisplayOptionPicker(
 | 
						|
            onSelect: handleOnSelect,
 | 
						|
            filter: filter.value.display,
 | 
						|
          ),
 | 
						|
        ),
 | 
						|
      );
 | 
						|
    }
 | 
						|
 | 
						|
    handleTextSubmitted(String value) {
 | 
						|
      if (isContextualSearch.value) {
 | 
						|
        filter.value = filter.value.copyWith(
 | 
						|
          context: value,
 | 
						|
          filename: null,
 | 
						|
        );
 | 
						|
      } else {
 | 
						|
        filter.value = filter.value.copyWith(filename: value, context: null);
 | 
						|
      }
 | 
						|
 | 
						|
      search();
 | 
						|
    }
 | 
						|
 | 
						|
    buildSearchResult() {
 | 
						|
      return switch (searchProvider) {
 | 
						|
        AsyncData() => Expanded(
 | 
						|
            child: Padding(
 | 
						|
              padding: const EdgeInsets.only(top: 8.0),
 | 
						|
              child: NotificationListener<ScrollEndNotification>(
 | 
						|
                onNotification: (notification) {
 | 
						|
                  final metrics = notification.metrics;
 | 
						|
                  final shouldLoadMore = searchResultCount.value > 75;
 | 
						|
                  if (metrics.pixels >= metrics.maxScrollExtent &&
 | 
						|
                      shouldLoadMore) {
 | 
						|
                    loadMoreSearchResult();
 | 
						|
                  }
 | 
						|
                  return true;
 | 
						|
                },
 | 
						|
                child: MultiselectGrid(
 | 
						|
                  renderListProvider: paginatedSearchRenderListProvider,
 | 
						|
                  archiveEnabled: true,
 | 
						|
                  deleteEnabled: true,
 | 
						|
                  editEnabled: true,
 | 
						|
                  favoriteEnabled: true,
 | 
						|
                  stackEnabled: false,
 | 
						|
                  emptyIndicator: const SizedBox(),
 | 
						|
                ),
 | 
						|
              ),
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
        AsyncError(:final error) => Text('Error: $error'),
 | 
						|
        _ => const Expanded(child: Center(child: CircularProgressIndicator())),
 | 
						|
      };
 | 
						|
    }
 | 
						|
 | 
						|
    return Scaffold(
 | 
						|
      resizeToAvoidBottomInset: true,
 | 
						|
      appBar: AppBar(
 | 
						|
        automaticallyImplyLeading: true,
 | 
						|
        actions: [
 | 
						|
          IconButton(
 | 
						|
            icon: isContextualSearch.value
 | 
						|
                ? const Icon(Icons.abc_rounded)
 | 
						|
                : const Icon(Icons.image_search_rounded),
 | 
						|
            onPressed: () {
 | 
						|
              isContextualSearch.value = !isContextualSearch.value;
 | 
						|
              textSearchController.clear();
 | 
						|
            },
 | 
						|
          ),
 | 
						|
        ],
 | 
						|
        leading: IconButton(
 | 
						|
          icon: const Icon(Icons.arrow_back_ios_new_rounded),
 | 
						|
          onPressed: () => context.router.maybePop(),
 | 
						|
        ),
 | 
						|
        title: TextField(
 | 
						|
          controller: textSearchController,
 | 
						|
          decoration: InputDecoration(
 | 
						|
            hintText: isContextualSearch.value
 | 
						|
                ? 'Sunrise on the beach'
 | 
						|
                : 'File name or extension',
 | 
						|
            hintStyle: context.textTheme.bodyLarge?.copyWith(
 | 
						|
              color: context.themeData.colorScheme.onSurface.withOpacity(0.75),
 | 
						|
              fontWeight: FontWeight.w500,
 | 
						|
            ),
 | 
						|
            enabledBorder: const UnderlineInputBorder(
 | 
						|
              borderSide: BorderSide(color: Colors.transparent),
 | 
						|
            ),
 | 
						|
            focusedBorder: const UnderlineInputBorder(
 | 
						|
              borderSide: BorderSide(color: Colors.transparent),
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
          onSubmitted: handleTextSubmitted,
 | 
						|
        ),
 | 
						|
      ),
 | 
						|
      body: Column(
 | 
						|
        children: [
 | 
						|
          Padding(
 | 
						|
            padding: const EdgeInsets.only(top: 12.0),
 | 
						|
            child: SizedBox(
 | 
						|
              height: 50,
 | 
						|
              child: ListView(
 | 
						|
                shrinkWrap: true,
 | 
						|
                scrollDirection: Axis.horizontal,
 | 
						|
                padding: const EdgeInsets.symmetric(horizontal: 16),
 | 
						|
                children: [
 | 
						|
                  SearchFilterChip(
 | 
						|
                    icon: Icons.people_alt_rounded,
 | 
						|
                    onTap: showPeoplePicker,
 | 
						|
                    label: 'People',
 | 
						|
                    currentFilter: peopleCurrentFilterWidget.value,
 | 
						|
                  ),
 | 
						|
                  SearchFilterChip(
 | 
						|
                    icon: Icons.location_pin,
 | 
						|
                    onTap: showLocationPicker,
 | 
						|
                    label: 'Location',
 | 
						|
                    currentFilter: locationCurrentFilterWidget.value,
 | 
						|
                  ),
 | 
						|
                  SearchFilterChip(
 | 
						|
                    icon: Icons.camera_alt_rounded,
 | 
						|
                    onTap: showCameraPicker,
 | 
						|
                    label: 'Camera',
 | 
						|
                    currentFilter: cameraCurrentFilterWidget.value,
 | 
						|
                  ),
 | 
						|
                  SearchFilterChip(
 | 
						|
                    icon: Icons.date_range_rounded,
 | 
						|
                    onTap: showDatePicker,
 | 
						|
                    label: 'Date',
 | 
						|
                    currentFilter: dateRangeCurrentFilterWidget.value,
 | 
						|
                  ),
 | 
						|
                  SearchFilterChip(
 | 
						|
                    icon: Icons.video_collection_outlined,
 | 
						|
                    onTap: showMediaTypePicker,
 | 
						|
                    label: 'Media Type',
 | 
						|
                    currentFilter: mediaTypeCurrentFilterWidget.value,
 | 
						|
                  ),
 | 
						|
                  SearchFilterChip(
 | 
						|
                    icon: Icons.display_settings_outlined,
 | 
						|
                    onTap: showDisplayOptionPicker,
 | 
						|
                    label: 'Display Options',
 | 
						|
                    currentFilter: displayOptionCurrentFilterWidget.value,
 | 
						|
                  ),
 | 
						|
                ],
 | 
						|
              ),
 | 
						|
            ),
 | 
						|
          ),
 | 
						|
          buildSearchResult(),
 | 
						|
        ],
 | 
						|
      ),
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 |