mirror of
https://github.com/immich-app/immich.git
synced 2025-03-11 02:23:09 -05:00
- Fix missing timezones - Remove the UTC prefix from timezone display text to align with web app - Remove unnecessary layout builder - Created a custom `DropdownSearchMenu` widget Co-authored-by: Alex <alex.tran1502@gmail.com>
169 lines
5.8 KiB
Dart
169 lines
5.8 KiB
Dart
import 'package:collection/collection.dart';
|
|
import 'package:easy_localization/easy_localization.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/scheduler.dart';
|
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|
|
|
class DropdownSearchMenu<T> extends HookWidget {
|
|
const DropdownSearchMenu({
|
|
super.key,
|
|
required this.dropdownMenuEntries,
|
|
this.initialSelection,
|
|
this.onSelected,
|
|
this.trailingIcon,
|
|
this.hintText,
|
|
this.label,
|
|
this.textStyle,
|
|
this.menuConstraints,
|
|
});
|
|
|
|
final List<DropdownMenuEntry<T>> dropdownMenuEntries;
|
|
final T? initialSelection;
|
|
final ValueChanged<T>? onSelected;
|
|
final Widget? trailingIcon;
|
|
final String? hintText;
|
|
final Widget? label;
|
|
final TextStyle? textStyle;
|
|
final BoxConstraints? menuConstraints;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final selectedItem = useState<DropdownMenuEntry<T>?>(
|
|
dropdownMenuEntries
|
|
.firstWhereOrNull((item) => item.value == initialSelection),
|
|
);
|
|
final showTimeZoneDropdown = useState<bool>(false);
|
|
|
|
final effectiveConstraints = menuConstraints ??
|
|
const BoxConstraints(
|
|
minWidth: 280,
|
|
maxWidth: 280,
|
|
minHeight: 0,
|
|
maxHeight: 280,
|
|
);
|
|
|
|
final inputDecoration = InputDecoration(
|
|
contentPadding: const EdgeInsets.fromLTRB(12, 4, 12, 4),
|
|
border: const OutlineInputBorder(),
|
|
suffixIcon: trailingIcon,
|
|
label: label,
|
|
hintText: hintText,
|
|
).applyDefaults(context.themeData.inputDecorationTheme);
|
|
|
|
if (!showTimeZoneDropdown.value) {
|
|
return ConstrainedBox(
|
|
constraints: effectiveConstraints,
|
|
child: GestureDetector(
|
|
onTap: () => showTimeZoneDropdown.value = true,
|
|
child: InputDecorator(
|
|
decoration: inputDecoration,
|
|
child: selectedItem.value != null
|
|
? Text(
|
|
selectedItem.value!.label,
|
|
maxLines: 1,
|
|
overflow: TextOverflow.ellipsis,
|
|
style: textStyle,
|
|
)
|
|
: null,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
return ConstrainedBox(
|
|
constraints: effectiveConstraints,
|
|
child: Autocomplete<DropdownMenuEntry<T>>(
|
|
displayStringForOption: (option) => option.label,
|
|
optionsBuilder: (textEditingValue) {
|
|
return dropdownMenuEntries.where(
|
|
(item) => item.label
|
|
.toLowerCase()
|
|
.trim()
|
|
.contains(textEditingValue.text.toLowerCase().trim()),
|
|
);
|
|
},
|
|
onSelected: (option) {
|
|
selectedItem.value = option;
|
|
showTimeZoneDropdown.value = false;
|
|
onSelected?.call(option.value);
|
|
},
|
|
fieldViewBuilder: (context, textEditingController, focusNode, _) {
|
|
return TextField(
|
|
autofocus: true,
|
|
focusNode: focusNode,
|
|
controller: textEditingController,
|
|
decoration: inputDecoration.copyWith(
|
|
hintText: "edit_date_time_dialog_search_timezone".tr(),
|
|
),
|
|
maxLines: 1,
|
|
style: context.textTheme.bodyMedium,
|
|
expands: false,
|
|
onTapOutside: (event) {
|
|
showTimeZoneDropdown.value = false;
|
|
focusNode.unfocus();
|
|
},
|
|
onSubmitted: (_) {
|
|
showTimeZoneDropdown.value = false;
|
|
},
|
|
);
|
|
},
|
|
optionsViewBuilder: (context, onSelected, options) {
|
|
// This widget is a copy of the default implementation.
|
|
// We have only changed the `constraints` parameter.
|
|
return Align(
|
|
alignment: Alignment.topLeft,
|
|
child: ConstrainedBox(
|
|
constraints: effectiveConstraints,
|
|
child: Material(
|
|
elevation: 4.0,
|
|
child: ListView.builder(
|
|
padding: EdgeInsets.zero,
|
|
shrinkWrap: true,
|
|
itemCount: options.length,
|
|
itemBuilder: (BuildContext context, int index) {
|
|
final option = options.elementAt(index);
|
|
return InkWell(
|
|
onTap: () => onSelected(option),
|
|
child: Builder(
|
|
builder: (BuildContext context) {
|
|
final bool highlight =
|
|
AutocompleteHighlightedOption.of(context) ==
|
|
index;
|
|
if (highlight) {
|
|
SchedulerBinding.instance.addPostFrameCallback(
|
|
(Duration timeStamp) {
|
|
Scrollable.ensureVisible(
|
|
context,
|
|
alignment: 0.5,
|
|
);
|
|
},
|
|
debugLabel: 'AutocompleteOptions.ensureVisible',
|
|
);
|
|
}
|
|
return Container(
|
|
color: highlight
|
|
? Theme.of(context)
|
|
.colorScheme
|
|
.onSurface
|
|
.withOpacity(0.12)
|
|
: null,
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Text(
|
|
option.label,
|
|
style: textStyle,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|