mirror of
https://github.com/immich-app/immich.git
synced 2025-05-24 01:12:58 -04:00
chore(mobile): search field in separate widget (#16977)
* chore(mobile): search field in separate widget * fix: removed unnecessary use of context * chore: minor styling tweak * fix: controller not bound --------- Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
parent
8bc80076bb
commit
a651a4bf0e
@ -17,6 +17,7 @@ import 'package:immich_mobile/routing/router.dart';
|
|||||||
import 'package:immich_mobile/widgets/album/album_thumbnail_card.dart';
|
import 'package:immich_mobile/widgets/album/album_thumbnail_card.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_app_bar.dart';
|
import 'package:immich_mobile/widgets/common/immich_app_bar.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
|
import 'package:immich_mobile/widgets/common/immich_thumbnail.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/search_field.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class AlbumsPage extends HookConsumerWidget {
|
class AlbumsPage extends HookConsumerWidget {
|
||||||
@ -115,46 +116,17 @@ class AlbumsPage extends HookConsumerWidget {
|
|||||||
transform: const GradientRotation(0.5 * pi),
|
transform: const GradientRotation(0.5 * pi),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: TextField(
|
child: SearchField(
|
||||||
autofocus: false,
|
autofocus: false,
|
||||||
decoration: InputDecoration(
|
contentPadding: const EdgeInsets.all(16),
|
||||||
contentPadding: const EdgeInsets.all(16),
|
hintText: 'search_albums'.tr(),
|
||||||
border: OutlineInputBorder(
|
prefixIcon: const Icon(Icons.search_rounded),
|
||||||
borderRadius: BorderRadius.circular(25),
|
suffixIcon: searchController.text.isNotEmpty
|
||||||
borderSide: BorderSide(
|
? IconButton(
|
||||||
color: context.colorScheme.surfaceDim,
|
icon: const Icon(Icons.clear_rounded),
|
||||||
),
|
onPressed: clearSearch,
|
||||||
),
|
)
|
||||||
enabledBorder: OutlineInputBorder(
|
: null,
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: context.colorScheme.surfaceContainer,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
disabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: context.colorScheme.surfaceDim,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: context.colorScheme.primary.withAlpha(100),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
hintText: 'search_albums'.tr(),
|
|
||||||
hintStyle: context.textTheme.bodyLarge?.copyWith(
|
|
||||||
color: context.colorScheme.onSurfaceSecondary,
|
|
||||||
),
|
|
||||||
prefixIcon: const Icon(Icons.search_rounded),
|
|
||||||
suffixIcon: searchController.text.isNotEmpty
|
|
||||||
? IconButton(
|
|
||||||
icon: const Icon(Icons.clear_rounded),
|
|
||||||
onPressed: clearSearch,
|
|
||||||
)
|
|
||||||
: const SizedBox.shrink(),
|
|
||||||
),
|
|
||||||
controller: searchController,
|
controller: searchController,
|
||||||
onChanged: (_) =>
|
onChanged: (_) =>
|
||||||
onSearch(searchController.text, filterMode.value),
|
onSearch(searchController.text, filterMode.value),
|
||||||
|
@ -8,13 +8,13 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:immich_mobile/constants/enums.dart';
|
import 'package:immich_mobile/constants/enums.dart';
|
||||||
import 'package:immich_mobile/entities/asset.entity.dart';
|
import 'package:immich_mobile/entities/asset.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
|
||||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
||||||
import 'package:immich_mobile/models/search/search_filter.model.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/providers/search/paginated_search.provider.dart';
|
||||||
import 'package:immich_mobile/providers/search/search_input_focus.provider.dart';
|
import 'package:immich_mobile/providers/search/search_input_focus.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart';
|
import 'package:immich_mobile/widgets/asset_grid/multiselect_grid.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/search_field.dart';
|
||||||
import 'package:immich_mobile/widgets/search/search_filter/camera_picker.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/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/filter_bottom_sheet_scaffold.dart';
|
||||||
@ -640,51 +640,21 @@ class SearchPage extends HookConsumerWidget {
|
|||||||
end: Alignment.bottomRight,
|
end: Alignment.bottomRight,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: TextField(
|
child: SearchField(
|
||||||
|
hintText: searchHintText.value,
|
||||||
key: const Key('search_text_field'),
|
key: const Key('search_text_field'),
|
||||||
controller: textSearchController,
|
controller: textSearchController,
|
||||||
decoration: InputDecoration(
|
contentPadding: prefilter != null
|
||||||
contentPadding: prefilter != null
|
? const EdgeInsets.only(left: 24)
|
||||||
? const EdgeInsets.only(left: 24)
|
: const EdgeInsets.all(8),
|
||||||
: const EdgeInsets.all(8),
|
prefixIcon: prefilter != null
|
||||||
prefixIcon: prefilter != null
|
? null
|
||||||
? null
|
: Icon(
|
||||||
: Icon(
|
getSearchPrefixIcon(),
|
||||||
getSearchPrefixIcon(),
|
color: context.colorScheme.primary,
|
||||||
color: context.colorScheme.primary,
|
),
|
||||||
),
|
|
||||||
hintText: searchHintText.value,
|
|
||||||
hintStyle: context.textTheme.bodyLarge?.copyWith(
|
|
||||||
color: context.themeData.colorScheme.onSurfaceSecondary,
|
|
||||||
),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: context.colorScheme.surfaceDim,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: context.colorScheme.surfaceContainer,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
disabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: context.colorScheme.surfaceDim,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: context.colorScheme.primary.withAlpha(100),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onSubmitted: handleTextSubmitted,
|
onSubmitted: handleTextSubmitted,
|
||||||
focusNode: ref.watch(searchInputFocusProvider),
|
focusNode: ref.watch(searchInputFocusProvider),
|
||||||
onTapOutside: (_) => ref.read(searchInputFocusProvider).unfocus(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
79
mobile/lib/widgets/common/search_field.dart
Normal file
79
mobile/lib/widgets/common/search_field.dart
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
||||||
|
|
||||||
|
class SearchField extends StatelessWidget {
|
||||||
|
const SearchField({
|
||||||
|
super.key,
|
||||||
|
required this.hintText,
|
||||||
|
this.autofocus = false,
|
||||||
|
this.controller,
|
||||||
|
this.focusNode,
|
||||||
|
this.onChanged,
|
||||||
|
this.onSubmitted,
|
||||||
|
this.onTapOutside,
|
||||||
|
this.contentPadding = const EdgeInsets.only(left: 24),
|
||||||
|
this.prefixIcon,
|
||||||
|
this.suffixIcon,
|
||||||
|
this.filled = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
final FocusNode? focusNode;
|
||||||
|
final void Function(String)? onChanged;
|
||||||
|
final void Function(String)? onSubmitted;
|
||||||
|
final void Function(PointerDownEvent)? onTapOutside;
|
||||||
|
final TextEditingController? controller;
|
||||||
|
final String hintText;
|
||||||
|
final EdgeInsetsGeometry contentPadding;
|
||||||
|
final Widget? prefixIcon;
|
||||||
|
final Widget? suffixIcon;
|
||||||
|
final bool autofocus;
|
||||||
|
final bool filled;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextField(
|
||||||
|
controller: controller,
|
||||||
|
autofocus: autofocus,
|
||||||
|
focusNode: focusNode,
|
||||||
|
onChanged: onChanged,
|
||||||
|
onTapOutside: onTapOutside ?? (_) => focusNode?.unfocus(),
|
||||||
|
onSubmitted: onSubmitted,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
contentPadding: contentPadding,
|
||||||
|
filled: filled,
|
||||||
|
fillColor: context.primaryColor.withValues(alpha: 0.1),
|
||||||
|
hintStyle: context.textTheme.bodyLarge?.copyWith(
|
||||||
|
color: context.themeData.colorScheme.onSurfaceSecondary,
|
||||||
|
),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(25),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: context.colorScheme.surfaceDim,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(25),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: context.colorScheme.surfaceContainer,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
disabledBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(25),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: context.colorScheme.surfaceDim,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(25),
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: context.colorScheme.primary.withAlpha(100),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
prefixIcon: prefixIcon,
|
||||||
|
suffixIcon: suffixIcon,
|
||||||
|
hintText: hintText,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -4,12 +4,12 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/theme_extensions.dart';
|
|
||||||
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
import 'package:immich_mobile/interfaces/person_api.interface.dart';
|
||||||
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
||||||
import 'package:immich_mobile/providers/search/people.provider.dart';
|
import 'package:immich_mobile/providers/search/people.provider.dart';
|
||||||
import 'package:immich_mobile/services/api.service.dart';
|
import 'package:immich_mobile/services/api.service.dart';
|
||||||
import 'package:immich_mobile/utils/image_url_builder.dart';
|
import 'package:immich_mobile/utils/image_url_builder.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/search_field.dart';
|
||||||
|
|
||||||
class PeoplePicker extends HookConsumerWidget {
|
class PeoplePicker extends HookConsumerWidget {
|
||||||
const PeoplePicker({super.key, required this.onSelect, this.filter});
|
const PeoplePicker({super.key, required this.onSelect, this.filter});
|
||||||
@ -30,47 +30,12 @@ class PeoplePicker extends HookConsumerWidget {
|
|||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: TextField(
|
child: SearchField(
|
||||||
focusNode: formFocus,
|
focusNode: formFocus,
|
||||||
onChanged: (value) => searchQuery.value = value,
|
onChanged: (value) => searchQuery.value = value,
|
||||||
onTapOutside: (_) => formFocus.unfocus(),
|
onTapOutside: (_) => formFocus.unfocus(),
|
||||||
decoration: InputDecoration(
|
filled: true,
|
||||||
contentPadding: const EdgeInsets.only(left: 24),
|
hintText: 'search_filter_people_hint'.tr(),
|
||||||
filled: true,
|
|
||||||
fillColor: context.primaryColor.withValues(alpha: 0.1),
|
|
||||||
hintStyle: context.textTheme.bodyLarge?.copyWith(
|
|
||||||
color: context.themeData.colorScheme.onSurfaceSecondary,
|
|
||||||
),
|
|
||||||
border: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: context.colorScheme.surfaceContainerHighest,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: context.colorScheme.surfaceContainerHighest,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
disabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: context.colorScheme.surfaceContainerHighest,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.circular(25),
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: context.colorScheme.primary.withAlpha(150),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
prefixIcon: Icon(
|
|
||||||
Icons.search_rounded,
|
|
||||||
color: context.colorScheme.primary,
|
|
||||||
),
|
|
||||||
hintText: 'search_filter_people_hint'.tr(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user