fix: Fix scrolling issue, update selection app bar

This commit is contained in:
Anton Stubenbord
2023-03-18 16:23:09 +01:00
parent 993f6af827
commit 78fbd042a6
7 changed files with 195 additions and 189 deletions

View File

@@ -23,8 +23,8 @@ class SliverSearchBar extends StatelessWidget {
floating: floating,
pinned: pinned,
delegate: CustomizableSliverPersistentHeaderDelegate(
minExtent: kToolbarHeight + 8,
maxExtent: kToolbarHeight + 8,
minExtent: kToolbarHeight,
maxExtent: kToolbarHeight,
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
child: SearchBar(

View File

@@ -114,31 +114,38 @@ class _DocumentsPageState extends State<DocumentsPage>
},
builder: (context, connectivityState) {
return SafeArea(
top: context.read<DocumentsCubit>().state.selection.isEmpty,
child: Scaffold(
drawer: const AppDrawer(),
floatingActionButton: BlocBuilder<DocumentsCubit, DocumentsState>(
builder: (context, state) {
final appliedFiltersCount = state.filter.appliedFiltersCount;
return b.Badge(
position: b.BadgePosition.topEnd(top: -12, end: -6),
showBadge: appliedFiltersCount > 0,
badgeContent: Text(
'$appliedFiltersCount',
style: const TextStyle(
color: Colors.white,
final show = state.selection.isEmpty;
return AnimatedScale(
scale: show ? 1 : 0,
duration: const Duration(milliseconds: 200),
curve: Curves.easeIn,
child: b.Badge(
position: b.BadgePosition.topEnd(top: -12, end: -6),
showBadge: appliedFiltersCount > 0,
badgeContent: Text(
'$appliedFiltersCount',
style: const TextStyle(
color: Colors.white,
),
),
animationType: b.BadgeAnimationType.fade,
badgeColor: Colors.red,
child: _currentTab == 0
? FloatingActionButton(
child: const Icon(Icons.filter_alt_outlined),
onPressed: _openDocumentFilter,
)
: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () => _onCreateSavedView(state.filter),
),
),
animationType: b.BadgeAnimationType.fade,
badgeColor: Colors.red,
child: _currentTab == 0
? FloatingActionButton(
child: const Icon(Icons.filter_alt_outlined),
onPressed: _openDocumentFilter,
)
: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () => _onCreateSavedView(state.filter),
),
);
},
),
@@ -296,7 +303,7 @@ class _DocumentsPageState extends State<DocumentsPage>
_currentTab != 0 ||
currState.isLoading ||
currState.isLastPageLoaded) {
return true;
return false;
}
final offset = notification.metrics.pixels;
@@ -311,6 +318,7 @@ class _DocumentsPageState extends State<DocumentsPage>
stackTrace,
),
);
return true;
}
return false;
},
@@ -361,46 +369,25 @@ class _DocumentsPageState extends State<DocumentsPage>
Widget _buildViewActions() {
return SliverToBoxAdapter(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const SortDocumentsButton(),
BlocBuilder<DocumentsCubit, DocumentsState>(
builder: (context, state) {
return ViewTypeSelectionWidget(
child: BlocBuilder<DocumentsCubit, DocumentsState>(
builder: (context, state) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SortDocumentsButton(
enabled: state.selection.isEmpty,
),
ViewTypeSelectionWidget(
viewType: state.viewType,
onChanged: context.read<DocumentsCubit>().setViewType,
);
},
)
],
),
],
);
},
).paddedSymmetrically(horizontal: 8, vertical: 4),
);
}
void _onDelete(DocumentsState documentsState) async {
final shouldDelete = await showDialog<bool>(
context: context,
builder: (context) =>
BulkDeleteConfirmationDialog(state: documentsState),
) ??
false;
if (shouldDelete) {
try {
await context
.read<DocumentsCubit>()
.bulkDelete(documentsState.selection);
showSnackBar(
context,
S.of(context)!.documentsSuccessfullyDeleted,
);
context.read<DocumentsCubit>().resetSelection();
} on PaperlessServerException catch (error, stackTrace) {
showErrorMessage(context, error, stackTrace);
}
}
}
void _onCreateSavedView(DocumentFilter filter) async {
final newView = await Navigator.of(context).push<SavedView?>(
MaterialPageRoute(

View File

@@ -44,6 +44,7 @@ class DocumentDetailedItem extends DocumentItem {
? min(600.0, availableHeight)
: min(500.0, availableHeight);
return Card(
color: isSelected ? Theme.of(context).colorScheme.inversePrimary : null,
child: InkWell(
enableFeedback: true,
borderRadius: BorderRadius.circular(12),

View File

@@ -30,106 +30,108 @@ class DocumentListItem extends DocumentItem {
@override
Widget build(BuildContext context) {
return DocumentTypeBlocProvider(
child: ListTile(
dense: true,
selected: isSelected,
onTap: () => _onTap(),
selectedTileColor: Theme.of(context).colorScheme.inversePrimary,
onLongPress: () => onSelected?.call(document),
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(
children: [
AbsorbPointer(
absorbing: isSelectionActive,
child: CorrespondentWidget(
isClickable: isLabelClickable,
correspondentId: document.correspondent,
onSelected: onCorrespondentSelected,
child: Material(
child: ListTile(
dense: true,
selected: isSelected,
onTap: () => _onTap(),
selectedTileColor: Theme.of(context).colorScheme.inversePrimary,
onLongPress: () => onSelected?.call(document),
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Row(
children: [
AbsorbPointer(
absorbing: isSelectionActive,
child: CorrespondentWidget(
isClickable: isLabelClickable,
correspondentId: document.correspondent,
onSelected: onCorrespondentSelected,
),
),
),
],
),
Text(
document.title,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
AbsorbPointer(
absorbing: isSelectionActive,
child: TagsWidget(
isClickable: isLabelClickable,
tagIds: document.tags,
isMultiLine: false,
onTagSelected: (id) => onTagSelected?.call(id),
],
),
Text(
document.title,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
AbsorbPointer(
absorbing: isSelectionActive,
child: TagsWidget(
isClickable: isLabelClickable,
tagIds: document.tags,
isMultiLine: false,
onTagSelected: (id) => onTagSelected?.call(id),
),
)
],
),
subtitle: Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: BlocBuilder<LabelCubit<DocumentType>,
LabelState<DocumentType>>(
builder: (context, docTypes) {
return RichText(
maxLines: 1,
overflow: TextOverflow.ellipsis,
text: TextSpan(
text: DateFormat.yMMMd().format(document.created),
style: Theme.of(context)
.textTheme
.labelSmall
?.apply(color: Colors.grey),
children: document.documentType != null
? [
const TextSpan(text: '\u30FB'),
TextSpan(
text: docTypes
.labels[document.documentType]?.name,
),
]
: null,
),
);
},
)
// Row(
// children: [
// Text(
// DateFormat.yMMMd().format(document.created),
// style: Theme.of(context)
// .textTheme
// .bodySmall
// ?.apply(color: Colors.grey),
// ),
// if (document.documentType != null) ...[
// Text("\u30FB"),
// DocumentTypeWidget(
// documentTypeId: document.documentType,
// textStyle: Theme.of(context).textTheme.bodySmall?.apply(
// color: Colors.grey,
// overflow: TextOverflow.ellipsis,
// ),
// ),
// ],
// ],
// ),
),
isThreeLine: document.tags.isNotEmpty,
leading: AspectRatio(
aspectRatio: _a4AspectRatio,
child: GestureDetector(
child: DocumentPreview(
document: document,
fit: BoxFit.cover,
alignment: Alignment.topCenter,
enableHero: enableHeroAnimation,
),
)
],
),
subtitle: Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child:
BlocBuilder<LabelCubit<DocumentType>, LabelState<DocumentType>>(
builder: (context, docTypes) {
return RichText(
maxLines: 1,
overflow: TextOverflow.ellipsis,
text: TextSpan(
text: DateFormat.yMMMd().format(document.created),
style: Theme.of(context)
.textTheme
.labelSmall
?.apply(color: Colors.grey),
children: document.documentType != null
? [
const TextSpan(text: '\u30FB'),
TextSpan(
text:
docTypes.labels[document.documentType]?.name,
),
]
: null,
),
);
},
)
// Row(
// children: [
// Text(
// DateFormat.yMMMd().format(document.created),
// style: Theme.of(context)
// .textTheme
// .bodySmall
// ?.apply(color: Colors.grey),
// ),
// if (document.documentType != null) ...[
// Text("\u30FB"),
// DocumentTypeWidget(
// documentTypeId: document.documentType,
// textStyle: Theme.of(context).textTheme.bodySmall?.apply(
// color: Colors.grey,
// overflow: TextOverflow.ellipsis,
// ),
// ),
// ],
// ],
// ),
),
isThreeLine: document.tags.isNotEmpty,
leading: AspectRatio(
aspectRatio: _a4AspectRatio,
child: GestureDetector(
child: DocumentPreview(
document: document,
fit: BoxFit.cover,
alignment: Alignment.topCenter,
enableHero: enableHeroAnimation,
),
),
contentPadding: const EdgeInsets.all(8.0),
),
contentPadding: const EdgeInsets.all(8.0),
),
);
}

View File

@@ -19,7 +19,11 @@ class DocumentSelectionSliverAppBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SliverAppBar(
stretch: false,
pinned: true,
floating: true,
snap: true,
backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
title: Text(
S.of(context)!.countSelected(state.selection.length),
),

View File

@@ -29,6 +29,10 @@ class ViewTypeSelectionWidget extends StatelessWidget {
}
return PopupMenuButton<ViewType>(
constraints: const BoxConstraints(
minWidth: 4 * 56.0,
maxWidth: 5 * 56.0,
), // Ensures text is not split into two lines
position: PopupMenuPosition.under,
initialValue: viewType,
icon: Icon(icon),
@@ -70,7 +74,10 @@ class ViewTypeSelectionWidget extends StatelessWidget {
child: ListTile(
selected: selected,
trailing: selected ? const Icon(Icons.done) : null,
title: Text(label),
title: Text(
label,
maxLines: 1,
),
iconColor: Theme.of(context).colorScheme.onSurface,
textColor: Theme.of(context).colorScheme.onSurface,
leading: Icon(icon),

View File

@@ -8,8 +8,10 @@ import 'package:paperless_mobile/features/documents/view/widgets/search/sort_fie
import 'package:paperless_mobile/features/labels/cubit/label_cubit.dart';
class SortDocumentsButton extends StatelessWidget {
final bool enabled;
const SortDocumentsButton({
super.key,
this.enabled = true,
});
@override
@@ -24,47 +26,50 @@ class SortDocumentsButton extends StatelessWidget {
? Icons.arrow_upward
: Icons.arrow_downward),
label: Text(translateSortField(context, state.filter.sortField)),
onPressed: () {
showModalBottomSheet(
elevation: 2,
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
builder: (_) => BlocProvider<DocumentsCubit>.value(
value: context.read<DocumentsCubit>(),
child: MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => LabelCubit<DocumentType>(
context.read<LabelRepository<DocumentType>>(),
onPressed: enabled
? () {
showModalBottomSheet(
elevation: 2,
context: context,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16),
),
),
BlocProvider(
create: (context) => LabelCubit<Correspondent>(
context.read<LabelRepository<Correspondent>>(),
),
),
],
child: SortFieldSelectionBottomSheet(
initialSortField: state.filter.sortField,
initialSortOrder: state.filter.sortOrder,
onSubmit: (field, order) =>
context.read<DocumentsCubit>().updateCurrentFilter(
(filter) => filter.copyWith(
sortField: field,
sortOrder: order,
),
builder: (_) => BlocProvider<DocumentsCubit>.value(
value: context.read<DocumentsCubit>(),
child: MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => LabelCubit<DocumentType>(
context.read<LabelRepository<DocumentType>>(),
),
),
),
),
);
},
),
BlocProvider(
create: (context) => LabelCubit<Correspondent>(
context.read<LabelRepository<Correspondent>>(),
),
),
],
child: SortFieldSelectionBottomSheet(
initialSortField: state.filter.sortField,
initialSortOrder: state.filter.sortOrder,
onSubmit: (field, order) => context
.read<DocumentsCubit>()
.updateCurrentFilter(
(filter) => filter.copyWith(
sortField: field,
sortOrder: order,
),
),
),
),
),
);
}
: null,
);
},
);