WIP - Refactoring date range picker dialog

This commit is contained in:
Anton Stubenbord
2022-12-19 03:03:11 +01:00
parent f77ccf50c1
commit 901d646ec2
27 changed files with 971 additions and 556 deletions
@@ -162,7 +162,7 @@ class _DocumentUploadPreparationPageState
S.of(context).documentCreatedPropertyLabel + " *",
),
),
LabelFormField<DocumentType, DocumentTypeQuery>(
LabelFormField<DocumentType>(
notAssignedSelectable: false,
formBuilderState: _formKey.currentState,
labelCreationWidgetBuilder: (initialName) =>
@@ -172,15 +172,13 @@ class _DocumentUploadPreparationPageState
),
child: AddDocumentTypePage(initialName: initialName),
),
label: S.of(context).documentDocumentTypePropertyLabel + " *",
textFieldLabel:
S.of(context).documentDocumentTypePropertyLabel + " *",
name: DocumentModel.documentTypeKey,
state: state.documentTypes,
queryParameterIdBuilder: DocumentTypeQuery.fromId,
queryParameterNotAssignedBuilder:
DocumentTypeQuery.notAssigned,
labelOptions: state.documentTypes,
prefixIcon: const Icon(Icons.description_outlined),
),
LabelFormField<Correspondent, CorrespondentQuery>(
LabelFormField<Correspondent>(
notAssignedSelectable: false,
formBuilderState: _formKey.currentState,
labelCreationWidgetBuilder: (initialName) =>
@@ -191,13 +189,10 @@ class _DocumentUploadPreparationPageState
),
child: AddCorrespondentPage(initialName: initialName),
),
label:
textFieldLabel:
S.of(context).documentCorrespondentPropertyLabel + " *",
name: DocumentModel.correspondentKey,
state: state.correspondents,
queryParameterIdBuilder: CorrespondentQuery.fromId,
queryParameterNotAssignedBuilder:
CorrespondentQuery.notAssigned,
labelOptions: state.correspondents,
prefixIcon: const Icon(Icons.person_outline),
),
TagFormField(
@@ -96,26 +96,24 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
Widget _buildStoragePathFormField(
int? initialId, Map<int, StoragePath> options) {
return LabelFormField<StoragePath, StoragePathQuery>(
return LabelFormField<StoragePath>(
notAssignedSelectable: false,
formBuilderState: _formKey.currentState,
labelCreationWidgetBuilder: (initialValue) => RepositoryProvider.value(
value: RepositoryProvider.of<LabelRepository<StoragePath>>(context),
child: AddStoragePathPage(initalValue: initialValue),
),
label: S.of(context).documentStoragePathPropertyLabel,
state: options,
initialValue: StoragePathQuery.fromId(initialId),
textFieldLabel: S.of(context).documentStoragePathPropertyLabel,
labelOptions: options,
initialValue: IdQueryParameter.fromId(initialId),
name: fkStoragePath,
queryParameterIdBuilder: StoragePathQuery.fromId,
queryParameterNotAssignedBuilder: StoragePathQuery.notAssigned,
prefixIcon: const Icon(Icons.folder_outlined),
);
}
Widget _buildCorrespondentFormField(
int? initialId, Map<int, Correspondent> options) {
return LabelFormField<Correspondent, CorrespondentQuery>(
return LabelFormField<Correspondent>(
notAssignedSelectable: false,
formBuilderState: _formKey.currentState,
labelCreationWidgetBuilder: (initialValue) => RepositoryProvider.value(
@@ -124,19 +122,17 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
),
child: AddCorrespondentPage(initialName: initialValue),
),
label: S.of(context).documentCorrespondentPropertyLabel,
state: options,
initialValue: CorrespondentQuery.fromId(initialId),
textFieldLabel: S.of(context).documentCorrespondentPropertyLabel,
labelOptions: options,
initialValue: IdQueryParameter.fromId(initialId),
name: fkCorrespondent,
queryParameterIdBuilder: CorrespondentQuery.fromId,
queryParameterNotAssignedBuilder: CorrespondentQuery.notAssigned,
prefixIcon: const Icon(Icons.person_outlined),
);
}
Widget _buildDocumentTypeFormField(
int? initialId, Map<int, DocumentType> options) {
return LabelFormField<DocumentType, DocumentTypeQuery>(
return LabelFormField<DocumentType>(
notAssignedSelectable: false,
formBuilderState: _formKey.currentState,
labelCreationWidgetBuilder: (currentInput) => RepositoryProvider.value(
@@ -147,12 +143,10 @@ class _DocumentEditPageState extends State<DocumentEditPage> {
initialName: currentInput,
),
),
label: S.of(context).documentDocumentTypePropertyLabel,
initialValue: DocumentTypeQuery.fromId(initialId),
state: options,
textFieldLabel: S.of(context).documentDocumentTypePropertyLabel,
initialValue: IdQueryParameter.fromId(initialId),
labelOptions: options,
name: fkDocumentType,
queryParameterIdBuilder: DocumentTypeQuery.fromId,
queryParameterNotAssignedBuilder: DocumentTypeQuery.notAssigned,
prefixIcon: const Icon(Icons.description_outlined),
);
}
@@ -121,6 +121,7 @@ class _DocumentsPageState extends State<DocumentsPage> {
expand: false,
snap: true,
initialChildSize: .9,
maxChildSize: .9,
builder: (context, controller) => LabelsBlocProvider(
child: DocumentFilterPanel(
initialFilter: _documentsCubit.state.filter,
@@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:intl/intl.dart';
import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_mobile/core/widgets/form_builder_fields/form_builder_extended_date_range_picker.dart';
import 'package:paperless_mobile/extensions/flutter_extensions.dart';
import 'package:paperless_mobile/features/documents/view/widgets/search/query_type_form_field.dart';
import 'package:paperless_mobile/features/labels/bloc/label_cubit.dart';
@@ -95,8 +96,13 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
style: Theme.of(context).textTheme.caption,
),
).padded(),
_buildCreatedDateRangePickerFormField(),
_buildAddedDateRangePickerFormField(),
FormBuilderExtendedDateRangePicker(
name: DocumentModel.createdKey,
initialValue: widget.initialFilter.created,
labelText: S.of(context).documentCreatedPropertyLabel,
).padded(),
// _buildCreatedDateRangePickerFormField(),
// _buildAddedDateRangePickerFormField(),
_buildCorrespondentFormField().padded(),
_buildDocumentTypeFormField().padded(),
_buildStoragePathFormField().padded(),
@@ -129,14 +135,12 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
Widget _buildDocumentTypeFormField() {
return BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>(
builder: (context, state) {
return LabelFormField<DocumentType, DocumentTypeQuery>(
return LabelFormField<DocumentType>(
formBuilderState: _formKey.currentState,
name: fkDocumentType,
state: state.labels,
label: S.of(context).documentDocumentTypePropertyLabel,
labelOptions: state.labels,
textFieldLabel: S.of(context).documentDocumentTypePropertyLabel,
initialValue: widget.initialFilter.documentType,
queryParameterIdBuilder: DocumentTypeQuery.fromId,
queryParameterNotAssignedBuilder: DocumentTypeQuery.notAssigned,
prefixIcon: const Icon(Icons.description_outlined),
);
},
@@ -146,14 +150,12 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
Widget _buildCorrespondentFormField() {
return BlocBuilder<LabelCubit<Correspondent>, LabelState<Correspondent>>(
builder: (context, state) {
return LabelFormField<Correspondent, CorrespondentQuery>(
return LabelFormField<Correspondent>(
formBuilderState: _formKey.currentState,
name: fkCorrespondent,
state: state.labels,
label: S.of(context).documentCorrespondentPropertyLabel,
labelOptions: state.labels,
textFieldLabel: S.of(context).documentCorrespondentPropertyLabel,
initialValue: widget.initialFilter.correspondent,
queryParameterIdBuilder: CorrespondentQuery.fromId,
queryParameterNotAssignedBuilder: CorrespondentQuery.notAssigned,
prefixIcon: const Icon(Icons.person_outline),
);
},
@@ -163,14 +165,12 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
Widget _buildStoragePathFormField() {
return BlocBuilder<LabelCubit<StoragePath>, LabelState<StoragePath>>(
builder: (context, state) {
return LabelFormField<StoragePath, StoragePathQuery>(
return LabelFormField<StoragePath>(
formBuilderState: _formKey.currentState,
name: fkStoragePath,
state: state.labels,
label: S.of(context).documentStoragePathPropertyLabel,
labelOptions: state.labels,
textFieldLabel: S.of(context).documentStoragePathPropertyLabel,
initialValue: widget.initialFilter.storagePath,
queryParameterIdBuilder: StoragePathQuery.fromId,
queryParameterNotAssignedBuilder: StoragePathQuery.notAssigned,
prefixIcon: const Icon(Icons.folder_outlined),
);
},
@@ -209,187 +209,99 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
);
}
Widget _buildDateRangePickerHelper(String formFieldKey) {
const spacer = SizedBox(width: 8.0);
return SizedBox(
height: 64,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
spacer,
ActionChip(
label: Text(
S.of(context).documentFilterDateRangeLastSevenDaysLabel,
),
onPressed: () {
_formKey.currentState?.fields[formFieldKey]?.didChange(
DateTimeRange(
start: DateUtils.addDaysToDate(DateTime.now(), -7),
end: DateTime.now(),
),
);
},
),
spacer,
ActionChip(
label: Text(
S.of(context).documentFilterDateRangeLastMonthLabel,
),
onPressed: () {
final now = DateTime.now();
final firstDayOfLastMonth =
DateUtils.addMonthsToMonthDate(now, -1);
_formKey.currentState?.fields[formFieldKey]?.didChange(
DateTimeRange(
start: DateTime(firstDayOfLastMonth.year,
firstDayOfLastMonth.month, now.day),
end: DateTime.now(),
),
);
},
),
spacer,
ActionChip(
label: Text(
S.of(context).documentFilterDateRangeLastThreeMonthsLabel,
),
onPressed: () {
final now = DateTime.now();
final firstDayOfLastMonth =
DateUtils.addMonthsToMonthDate(now, -3);
_formKey.currentState?.fields[formFieldKey]?.didChange(
DateTimeRange(
start: DateTime(
firstDayOfLastMonth.year,
firstDayOfLastMonth.month,
now.day,
),
end: DateTime.now(),
),
);
},
),
spacer,
ActionChip(
label: Text(
S.of(context).documentFilterDateRangeLastYearLabel,
),
onPressed: () {
final now = DateTime.now();
final firstDayOfLastMonth =
DateUtils.addMonthsToMonthDate(now, -12);
_formKey.currentState?.fields[formFieldKey]?.didChange(
DateTimeRange(
start: DateTime(
firstDayOfLastMonth.year,
firstDayOfLastMonth.month,
now.day,
),
end: DateTime.now(),
),
);
},
),
spacer,
],
),
);
}
// Widget _buildCreatedDateRangePickerFormField() {
// return Column(
// children: [
// FormBuilderDateRangePicker(
// initialValue: _dateTimeRangeOfNullable(
// widget.initialFilter.createdDateAfter,
// widget.initialFilter.createdDateBefore,
// ),
// // Workaround for theme data not being correctly passed to daterangepicker, see
// // https://github.com/flutter/flutter/issues/87580
// pickerBuilder: (context, Widget? child) => Theme(
// data: Theme.of(context).copyWith(
// dialogBackgroundColor: Theme.of(context).scaffoldBackgroundColor,
// appBarTheme: Theme.of(context).appBarTheme.copyWith(
// iconTheme:
// IconThemeData(color: Theme.of(context).primaryColor),
// ),
// colorScheme: Theme.of(context).colorScheme.copyWith(
// onPrimary: Theme.of(context).primaryColor,
// primary: Theme.of(context).colorScheme.primary,
// ),
// ),
// child: child!,
// ),
// format: DateFormat.yMMMd(Localizations.localeOf(context).toString()),
// fieldStartLabelText:
// S.of(context).documentFilterDateRangeFieldStartLabel,
// fieldEndLabelText: S.of(context).documentFilterDateRangeFieldEndLabel,
// firstDate: DateTime.fromMicrosecondsSinceEpoch(0),
// lastDate: DateTime.now(),
// name: fkCreatedAt,
// decoration: InputDecoration(
// prefixIcon: const Icon(Icons.calendar_month_outlined),
// labelText: S.of(context).documentCreatedPropertyLabel,
// suffixIcon: IconButton(
// icon: const Icon(Icons.clear),
// onPressed: () {
// _formKey.currentState?.fields[fkCreatedAt]?.didChange(null);
// },
// ),
// ),
// ).paddedSymmetrically(horizontal: 8, vertical: 4.0),
// ],
// );
// }
Widget _buildCreatedDateRangePickerFormField() {
return Column(
children: [
FormBuilderDateRangePicker(
initialValue: _dateTimeRangeOfNullable(
widget.initialFilter.createdDateAfter,
widget.initialFilter.createdDateBefore,
),
// Workaround for theme data not being correctly passed to daterangepicker, see
// https://github.com/flutter/flutter/issues/87580
pickerBuilder: (context, Widget? child) => Theme(
data: Theme.of(context).copyWith(
dialogBackgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBarTheme: Theme.of(context).appBarTheme.copyWith(
iconTheme:
IconThemeData(color: Theme.of(context).primaryColor),
),
colorScheme: Theme.of(context).colorScheme.copyWith(
onPrimary: Theme.of(context).primaryColor,
primary: Theme.of(context).colorScheme.primary,
),
),
child: child!,
),
format: DateFormat.yMMMd(Localizations.localeOf(context).toString()),
fieldStartLabelText:
S.of(context).documentFilterDateRangeFieldStartLabel,
fieldEndLabelText: S.of(context).documentFilterDateRangeFieldEndLabel,
firstDate: DateTime.fromMicrosecondsSinceEpoch(0),
lastDate: DateTime.now(),
name: fkCreatedAt,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.calendar_month_outlined),
labelText: S.of(context).documentCreatedPropertyLabel,
suffixIcon: IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
_formKey.currentState?.fields[fkCreatedAt]?.didChange(null);
},
),
),
).paddedSymmetrically(horizontal: 8, vertical: 4.0),
_buildDateRangePickerHelper(fkCreatedAt),
],
);
}
Widget _buildAddedDateRangePickerFormField() {
return Column(
children: [
FormBuilderDateRangePicker(
initialValue: _dateTimeRangeOfNullable(
widget.initialFilter.addedDateAfter,
widget.initialFilter.addedDateBefore,
),
// Workaround for theme data not being correctly passed to daterangepicker, see
// https://github.com/flutter/flutter/issues/87580
pickerBuilder: (context, Widget? child) => Theme(
data: Theme.of(context).copyWith(
dialogBackgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBarTheme: Theme.of(context).appBarTheme.copyWith(
iconTheme:
IconThemeData(color: Theme.of(context).primaryColor),
),
colorScheme: Theme.of(context).colorScheme.copyWith(
onPrimary: Theme.of(context).primaryColor,
primary: Theme.of(context).colorScheme.primary,
),
),
child: child!,
),
format: DateFormat.yMMMd(),
fieldStartLabelText:
S.of(context).documentFilterDateRangeFieldStartLabel,
fieldEndLabelText: S.of(context).documentFilterDateRangeFieldEndLabel,
firstDate: DateTime.fromMicrosecondsSinceEpoch(0),
lastDate: DateTime.now(),
name: fkAddedAt,
decoration: InputDecoration(
prefixIcon: const Icon(Icons.calendar_month_outlined),
labelText: S.of(context).documentAddedPropertyLabel,
suffixIcon: IconButton(
icon: const Icon(Icons.clear),
onPressed: () {
_formKey.currentState?.fields[fkAddedAt]?.didChange(null);
},
),
),
).paddedSymmetrically(horizontal: 8),
const SizedBox(height: 4.0),
_buildDateRangePickerHelper(fkAddedAt),
],
);
}
// Widget _buildAddedDateRangePickerFormField() {
// return Column(
// children: [
// FormBuilderDateRangePicker(
// initialValue: _dateTimeRangeOfNullable(
// widget.initialFilter.addedDateAfter,
// widget.initialFilter.addedDateBefore,
// ),
// // Workaround for theme data not being correctly passed to daterangepicker, see
// // https://github.com/flutter/flutter/issues/87580
// pickerBuilder: (context, Widget? child) => Theme(
// data: Theme.of(context).copyWith(
// dialogBackgroundColor: Theme.of(context).scaffoldBackgroundColor,
// appBarTheme: Theme.of(context).appBarTheme.copyWith(
// iconTheme:
// IconThemeData(color: Theme.of(context).primaryColor),
// ),
// colorScheme: Theme.of(context).colorScheme.copyWith(
// onPrimary: Theme.of(context).primaryColor,
// primary: Theme.of(context).colorScheme.primary,
// ),
// ),
// child: child!,
// ),
// format: DateFormat.yMMMd(),
// fieldStartLabelText:
// S.of(context).documentFilterDateRangeFieldStartLabel,
// fieldEndLabelText: S.of(context).documentFilterDateRangeFieldEndLabel,
// firstDate: DateTime.fromMicrosecondsSinceEpoch(0),
// lastDate: DateTime.now(),
// name: fkAddedAt,
// decoration: InputDecoration(
// prefixIcon: const Icon(Icons.calendar_month_outlined),
// labelText: S.of(context).documentAddedPropertyLabel,
// suffixIcon: IconButton(
// icon: const Icon(Icons.clear),
// onPressed: () {
// _formKey.currentState?.fields[fkAddedAt]?.didChange(null);
// },
// ),
// ),
// ).paddedSymmetrically(horizontal: 8),
// const SizedBox(height: 4.0),
// _buildDateRangePickerHelper(fkAddedAt),
// ],
// );
// }
void _onApplyFilter() async {
_formKey.currentState?.save();
@@ -408,19 +320,17 @@ class _DocumentFilterPanelState extends State<DocumentFilterPanel> {
DocumentFilter _assembleFilter() {
final v = _formKey.currentState!.value;
return DocumentFilter(
createdDateBefore: (v[fkCreatedAt] as DateTimeRange?)?.end,
createdDateAfter: (v[fkCreatedAt] as DateTimeRange?)?.start,
correspondent: v[fkCorrespondent] as CorrespondentQuery? ??
created: (v[fkCreatedAt] as DateRangeQuery),
correspondent: v[fkCorrespondent] as IdQueryParameter? ??
DocumentFilter.initial.correspondent,
documentType: v[fkDocumentType] as DocumentTypeQuery? ??
documentType: v[fkDocumentType] as IdQueryParameter? ??
DocumentFilter.initial.documentType,
storagePath: v[fkStoragePath] as StoragePathQuery? ??
storagePath: v[fkStoragePath] as IdQueryParameter? ??
DocumentFilter.initial.storagePath,
tags:
v[DocumentModel.tagsKey] as TagsQuery? ?? DocumentFilter.initial.tags,
queryText: v[fkQuery] as String?,
addedDateBefore: (v[fkAddedAt] as DateTimeRange?)?.end,
addedDateAfter: (v[fkAddedAt] as DateTimeRange?)?.start,
added: (v[fkAddedAt] as DateRangeQuery),
queryType: v[QueryTypeFormField.fkQueryType] as QueryType,
asnQuery: widget.initialFilter.asnQuery,
page: 1,
@@ -52,12 +52,12 @@ class CorrespondentWidget extends StatelessWidget {
if (cubit.state.filter.correspondent.id == correspondentId) {
cubit.updateCurrentFilter(
(filter) =>
filter.copyWith(correspondent: const CorrespondentQuery.unset()),
filter.copyWith(correspondent: const IdQueryParameter.unset()),
);
} else {
cubit.updateCurrentFilter(
(filter) => filter.copyWith(
correspondent: CorrespondentQuery.fromId(correspondentId)),
correspondent: IdQueryParameter.fromId(correspondentId)),
);
}
afterSelected?.call();
@@ -51,12 +51,12 @@ class DocumentTypeWidget extends StatelessWidget {
if (cubit.state.filter.documentType.id == documentTypeId) {
cubit.updateCurrentFilter(
(filter) =>
filter.copyWith(documentType: const DocumentTypeQuery.unset()),
filter.copyWith(documentType: const IdQueryParameter.unset()),
);
} else {
cubit.updateCurrentFilter(
(filter) => filter.copyWith(
documentType: DocumentTypeQuery.fromId(documentTypeId)),
documentType: IdQueryParameter.fromId(documentTypeId)),
);
}
afterSelected?.call();
@@ -54,12 +54,12 @@ class StoragePathWidget extends StatelessWidget {
if (cubit.state.filter.correspondent.id == pathId) {
cubit.updateCurrentFilter(
(filter) =>
filter.copyWith(storagePath: const StoragePathQuery.unset()),
filter.copyWith(storagePath: const IdQueryParameter.unset()),
);
} else {
cubit.updateCurrentFilter(
(filter) =>
filter.copyWith(storagePath: StoragePathQuery.fromId(pathId)),
filter.copyWith(storagePath: IdQueryParameter.fromId(pathId)),
);
}
afterSelected?.call();
@@ -126,7 +126,7 @@ class _LabelsPageState extends State<LabelsPage>
),
child: LabelTabView<Correspondent>(
filterBuilder: (label) => DocumentFilter(
correspondent: CorrespondentQuery.fromId(label.id),
correspondent: IdQueryParameter.fromId(label.id),
pageSize: label.documentCount ?? 0,
),
onEdit: _openEditCorrespondentPage,
@@ -146,7 +146,7 @@ class _LabelsPageState extends State<LabelsPage>
),
child: LabelTabView<DocumentType>(
filterBuilder: (label) => DocumentFilter(
documentType: DocumentTypeQuery.fromId(label.id),
documentType: IdQueryParameter.fromId(label.id),
pageSize: label.documentCount ?? 0,
),
onEdit: _openEditDocumentTypePage,
@@ -194,7 +194,7 @@ class _LabelsPageState extends State<LabelsPage>
child: LabelTabView<StoragePath>(
onEdit: _openEditStoragePathPage,
filterBuilder: (label) => DocumentFilter(
storagePath: StoragePathQuery.fromId(label.id),
storagePath: IdQueryParameter.fromId(label.id),
pageSize: label.documentCount ?? 0,
),
contentBuilder: (path) => Text(path.path ?? ""),
@@ -9,31 +9,26 @@ import 'package:paperless_mobile/generated/l10n.dart';
/// Form field allowing to select labels (i.e. correspondent, documentType)
/// [T] is the label type (e.g. [DocumentType], [Correspondent], ...), [R] is the return type (e.g. [CorrespondentQuery], ...).
///
class LabelFormField<T extends Label, R extends IdQueryParameter>
extends StatefulWidget {
class LabelFormField<T extends Label> extends StatefulWidget {
final Widget prefixIcon;
final Map<int, T> state;
final Map<int, T> labelOptions;
final FormBuilderState? formBuilderState;
final IdQueryParameter? initialValue;
final String name;
final String label;
final String textFieldLabel;
final FormFieldValidator? validator;
final Widget Function(String)? labelCreationWidgetBuilder;
final R Function() queryParameterNotAssignedBuilder;
final R Function(int? id) queryParameterIdBuilder;
final Widget Function(String initialName)? labelCreationWidgetBuilder;
final bool notAssignedSelectable;
final void Function(R?)? onChanged;
final void Function(IdQueryParameter?)? onChanged;
const LabelFormField({
Key? key,
required this.name,
required this.state,
required this.labelOptions,
this.validator,
this.initialValue,
required this.label,
required this.textFieldLabel,
this.labelCreationWidgetBuilder,
required this.queryParameterNotAssignedBuilder,
required this.queryParameterIdBuilder,
required this.formBuilderState,
required this.prefixIcon,
this.notAssignedSelectable = true,
@@ -41,11 +36,10 @@ class LabelFormField<T extends Label, R extends IdQueryParameter>
}) : super(key: key);
@override
State<LabelFormField<T, R>> createState() => _LabelFormFieldState<T, R>();
State<LabelFormField<T>> createState() => _LabelFormFieldState<T>();
}
class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
extends State<LabelFormField<T, R>> {
class _LabelFormFieldState<T extends Label> extends State<LabelFormField<T>> {
bool _showCreationSuffixIcon = false;
late bool _showClearSuffixIcon;
@@ -54,12 +48,13 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
@override
void initState() {
super.initState();
_showClearSuffixIcon = widget.state.containsKey(widget.initialValue?.id);
_showClearSuffixIcon =
widget.labelOptions.containsKey(widget.initialValue?.id);
_textEditingController = TextEditingController(
text: widget.state[widget.initialValue?.id]?.name ?? '',
text: widget.labelOptions[widget.initialValue?.id]?.name ?? '',
)..addListener(() {
setState(() {
_showCreationSuffixIcon = widget.state.values
_showCreationSuffixIcon = widget.labelOptions.values
.where(
(item) => item.name.toLowerCase().startsWith(
_textEditingController.text.toLowerCase(),
@@ -74,7 +69,7 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
@override
Widget build(BuildContext context) {
final isEnabled = widget.state.values.fold<bool>(
final isEnabled = widget.labelOptions.values.fold<bool>(
false,
(previousValue, element) =>
previousValue || (element.documentCount ?? 0) > 0) ||
@@ -90,7 +85,7 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
TextStyle(color: Theme.of(context).disabledColor, fontSize: 18.0),
),
),
initialValue: widget.initialValue ?? widget.queryParameterIdBuilder(null),
initialValue: widget.initialValue ?? const IdQueryParameter.unset(),
name: widget.name,
suggestionsBoxDecoration: SuggestionsBoxDecoration(
shape: RoundedRectangleBorder(
@@ -103,7 +98,7 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
),
itemBuilder: (context, suggestion) => ListTile(
title: Text(
widget.state[suggestion.id]?.name ??
widget.labelOptions[suggestion.id]?.name ??
S.of(context).labelNotAssignedText,
maxLines: 1,
overflow: TextOverflow.ellipsis,
@@ -112,10 +107,10 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
style: ListTileStyle.list,
),
suggestionsCallback: (pattern) {
final List<IdQueryParameter> suggestions = widget.state.entries
final List<IdQueryParameter> suggestions = widget.labelOptions.entries
.where(
(entry) =>
widget.state[entry.key]!.name
widget.labelOptions[entry.key]!.name
.toLowerCase()
.contains(pattern.toLowerCase()) ||
pattern.isEmpty,
@@ -125,34 +120,33 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
widget.labelCreationWidgetBuilder != null ||
(entry.value.documentCount ?? 0) > 0,
)
.map((entry) => widget.queryParameterIdBuilder(entry.key))
.map((entry) => IdQueryParameter.fromId(entry.key))
.toList();
if (widget.notAssignedSelectable) {
suggestions.insert(0, widget.queryParameterNotAssignedBuilder());
suggestions.insert(0, const IdQueryParameter.notAssigned());
}
return suggestions;
},
onChanged: (value) {
setState(() => _showClearSuffixIcon = value?.isSet ?? false);
widget.onChanged?.call(value as R);
widget.onChanged?.call(value);
},
controller: _textEditingController,
decoration: InputDecoration(
prefixIcon: widget.prefixIcon,
label: Text(widget.label),
label: Text(widget.textFieldLabel),
hintText: _getLocalizedHint(context),
suffixIcon: _buildSuffixIcon(context),
),
selectionToTextTransformer: (suggestion) {
if (suggestion == widget.queryParameterNotAssignedBuilder()) {
if (suggestion == const IdQueryParameter.notAssigned()) {
return S.of(context).labelNotAssignedText;
}
return widget.state[suggestion.id]?.name ?? "";
return widget.labelOptions[suggestion.id]?.name ?? "";
},
direction: AxisDirection.up,
onSuggestionSelected: (suggestion) => widget
.formBuilderState?.fields[widget.name]
?.didChange(suggestion as R),
onSuggestionSelected: (suggestion) =>
widget.formBuilderState?.fields[widget.name]?.didChange(suggestion),
);
}
@@ -161,7 +155,7 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
return IconButton(
onPressed: () async {
FocusScope.of(context).unfocus();
final createdLabel = await showDialog(
final createdLabel = await showDialog<T>(
context: context,
builder: (context) => widget.labelCreationWidgetBuilder!(
_textEditingController.text,
@@ -170,7 +164,7 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
if (createdLabel != null) {
// If new label has been created, set form field value and text of this form field and unfocus keyboard (we assume user is done).
widget.formBuilderState?.fields[widget.name]
?.didChange(widget.queryParameterIdBuilder(createdLabel.id));
?.didChange(IdQueryParameter.fromId(createdLabel.id));
_textEditingController.text = createdLabel.name;
} else {
_reset();
@@ -192,7 +186,7 @@ class _LabelFormFieldState<T extends Label, R extends IdQueryParameter>
void _reset() {
widget.formBuilderState?.fields[widget.name]?.didChange(
widget.queryParameterIdBuilder(null), // equivalnt to IdQueryParam.unset()
const IdQueryParameter.unset(),
);
_textEditingController.clear();
}