From e5af3a1b4b8a1ca8350f52ce206b141f104f54d6 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Wed, 5 Oct 2022 13:41:15 +0200 Subject: [PATCH] neon: Cleanup listviews --- .../src/apps/files/widgets/browser_view.dart | 264 ++++++++---------- packages/neon/lib/src/apps/news/app.dart | 1 - .../src/apps/news/widgets/articles_view.dart | 76 ++--- .../lib/src/apps/news/widgets/feeds_view.dart | 74 ++--- .../src/apps/news/widgets/folders_view.dart | 78 ++---- packages/neon/lib/src/apps/notes/app.dart | 1 - .../apps/notes/widgets/categories_view.dart | 70 ++--- .../src/apps/notes/widgets/notes_view.dart | 81 +++--- .../neon/lib/src/apps/notifications/app.dart | 1 - .../src/apps/notifications/pages/main.dart | 40 +-- .../neon/lib/src/utils/sort_box_builder.dart | 6 +- .../neon/lib/src/widgets/custom_listview.dart | 60 +++- 12 files changed, 329 insertions(+), 423 deletions(-) diff --git a/packages/neon/lib/src/apps/files/widgets/browser_view.dart b/packages/neon/lib/src/apps/files/widgets/browser_view.dart index 2b3b51d6..3e13af79 100644 --- a/packages/neon/lib/src/apps/files/widgets/browser_view.dart +++ b/packages/neon/lib/src/apps/files/widgets/browser_view.dart @@ -80,157 +80,133 @@ class _FilesBrowserViewState extends State { child: const Icon(Icons.add), ) : null, - body: RefreshIndicator( + body: CustomListView( + scrollKey: 'files-${pathSnapshot.data!.join('/')}', + withFloatingActionButton: true, + items: [ + for (final uploadTask in filesData == null + ? [] + : uploadTasksSnapshot.data!.where( + (final task) => filesData + .where((final file) => _pathMatchesFile(task.path, file.name)) + .isEmpty, + )) ...[ + StreamBuilder( + stream: uploadTask.progress, + builder: (final context, final uploadTaskProgressSnapshot) => + !uploadTaskProgressSnapshot.hasData + ? Container() + : _buildFile( + context: context, + details: FileDetails( + path: uploadTask.path, + isDirectory: false, + size: uploadTask.size, + etag: null, + mimeType: null, + lastModified: uploadTask.lastModified, + hasPreview: null, + isFavorite: null, + ), + uploadProgress: uploadTaskProgressSnapshot.data!, + downloadProgress: null, + ), + ), + ], + if (filesData != null) ...[ + for (final file in filesData) ...[ + if (!widget.onlyShowDirectories || file.isDirectory) ...[ + Builder( + builder: (final context) { + final matchingUploadTasks = uploadTasksSnapshot.data! + .where((final task) => _pathMatchesFile(task.path, file.name)); + final matchingDownloadTasks = downloadTasksSnapshot.data! + .where((final task) => _pathMatchesFile(task.path, file.name)); + + return StreamBuilder( + stream: matchingUploadTasks.isNotEmpty + ? matchingUploadTasks.first.progress + : Stream.value(null), + builder: (final context, final uploadTaskProgressSnapshot) => + StreamBuilder( + stream: matchingDownloadTasks.isNotEmpty + ? matchingDownloadTasks.first.progress + : Stream.value(null), + builder: (final context, final downloadTaskProgressSnapshot) => _buildFile( + context: context, + details: FileDetails( + path: [...widget.bloc.path.value, file.name], + isDirectory: matchingUploadTasks.isEmpty && file.isDirectory, + size: matchingUploadTasks.isNotEmpty + ? matchingUploadTasks.first.size + : file.size!, + etag: matchingUploadTasks.isNotEmpty ? null : file.etag, + mimeType: matchingUploadTasks.isNotEmpty ? null : file.mimeType, + lastModified: matchingUploadTasks.isNotEmpty + ? matchingUploadTasks.first.lastModified + : file.lastModified!, + hasPreview: matchingUploadTasks.isNotEmpty ? null : file.hasPreview, + isFavorite: matchingUploadTasks.isNotEmpty ? null : file.favorite, + ), + uploadProgress: uploadTaskProgressSnapshot.data, + downloadProgress: downloadTaskProgressSnapshot.data, + ), + ), + ); + }, + ), + ], + ], + ], + ], + isLoading: filesLoading, + error: filesError, + onRetry: () { + widget.bloc.refresh(); + }, onRefresh: () async { widget.bloc.refresh(); }, - child: Column( - children: [ - ExceptionWidget( - filesError, - onRetry: () { - widget.bloc.refresh(); - }, - ), - CustomLinearProgressIndicator( - visible: filesLoading, - ), - Align( - alignment: Alignment.topLeft, - child: Container( - margin: const EdgeInsets.symmetric( - horizontal: 10, - ), - child: Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - SizedBox( - height: 40, - child: InkWell( - onTap: () { - widget.bloc.setPath([]); - }, - child: const Icon(Icons.house), - ), + builder: (final context, final widget) => widget, + topScrollingChildren: [ + Align( + alignment: Alignment.topLeft, + child: Container( + margin: const EdgeInsets.symmetric( + horizontal: 10, + ), + child: Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + SizedBox( + height: 40, + child: InkWell( + onTap: () { + widget.bloc.setPath([]); + }, + child: const Icon(Icons.house), + ), + ), + for (var i = 0; i < pathSnapshot.data!.length; i++) ...[ + InkWell( + onTap: () { + widget.bloc.setPath(pathSnapshot.data!.sublist(0, i + 1)); + }, + child: Text(pathSnapshot.data![i]), ), - for (var i = 0; i < pathSnapshot.data!.length; i++) ...[ - InkWell( - onTap: () { - widget.bloc.setPath(pathSnapshot.data!.sublist(0, i + 1)); - }, - child: Text(pathSnapshot.data![i]), + ], + ] + .intersperse( + const Icon( + Icons.keyboard_arrow_right, + size: 40, ), - ], - ] - .intersperse( - const Icon( - Icons.keyboard_arrow_right, - size: 40, - ), - ) - .toList(), - ), + ) + .toList(), ), ), - if (filesData != null) ...[ - Builder( - builder: (final context) { - final uploadTasksWithoutExistingFile = uploadTasksSnapshot.data!.where( - (final task) => filesData - .where((final file) => _pathMatchesFile(task.path, file.name)) - .isEmpty, - ); - final widgets = [ - for (final uploadTask in uploadTasksWithoutExistingFile) ...[ - StreamBuilder( - stream: uploadTask.progress, - builder: (final context, final uploadTaskProgressSnapshot) => - !uploadTaskProgressSnapshot.hasData - ? Container() - : _buildFile( - context: context, - details: FileDetails( - path: uploadTask.path, - isDirectory: false, - size: uploadTask.size, - etag: null, - mimeType: null, - lastModified: uploadTask.lastModified, - hasPreview: null, - isFavorite: null, - ), - uploadProgress: uploadTaskProgressSnapshot.data!, - downloadProgress: null, - ), - ), - ], - for (final file in filesData) ...[ - if (!widget.onlyShowDirectories || file.isDirectory) ...[ - Builder( - builder: (final context) { - final matchingUploadTasks = uploadTasksSnapshot.data! - .where((final task) => _pathMatchesFile(task.path, file.name)); - final matchingDownloadTasks = downloadTasksSnapshot.data! - .where((final task) => _pathMatchesFile(task.path, file.name)); - - return StreamBuilder( - stream: matchingUploadTasks.isNotEmpty - ? matchingUploadTasks.first.progress - : Stream.value(null), - builder: (final context, final uploadTaskProgressSnapshot) => - StreamBuilder( - stream: matchingDownloadTasks.isNotEmpty - ? matchingDownloadTasks.first.progress - : Stream.value(null), - builder: (final context, final downloadTaskProgressSnapshot) => - _buildFile( - context: context, - details: FileDetails( - path: [...widget.bloc.path.value, file.name], - isDirectory: matchingUploadTasks.isEmpty && file.isDirectory, - size: matchingUploadTasks.isNotEmpty - ? matchingUploadTasks.first.size - : file.size!, - etag: matchingUploadTasks.isNotEmpty ? null : file.etag, - mimeType: matchingUploadTasks.isNotEmpty ? null : file.mimeType, - lastModified: matchingUploadTasks.isNotEmpty - ? matchingUploadTasks.first.lastModified - : file.lastModified!, - hasPreview: - matchingUploadTasks.isNotEmpty ? null : file.hasPreview, - isFavorite: - matchingUploadTasks.isNotEmpty ? null : file.favorite, - ), - uploadProgress: uploadTaskProgressSnapshot.data, - downloadProgress: downloadTaskProgressSnapshot.data, - ), - ), - ); - }, - ), - ], - ], - ]; - - return Expanded( - child: CustomListView( - scrollKey: 'files-${pathSnapshot.data!.join('/')}', - withFloatingActionButton: true, - items: widgets, - builder: (final context, final widget) => widget, - ), - ); - }, - ), - ], - ] - .intersperse( - const SizedBox( - height: 10, - ), - ) - .toList(), - ), + ), + ], ), ), ), diff --git a/packages/neon/lib/src/apps/news/app.dart b/packages/neon/lib/src/apps/news/app.dart index 9b8fb1a4..6c7fd187 100644 --- a/packages/neon/lib/src/apps/news/app.dart +++ b/packages/neon/lib/src/apps/news/app.dart @@ -8,7 +8,6 @@ import 'package:flutter_html/flutter_html.dart'; import 'package:flutter_rx_bloc/flutter_rx_bloc.dart'; import 'package:html/dom.dart' as html_dom; import 'package:html/parser.dart' as html_parser; -import 'package:intersperse/intersperse.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/apps/news/blocs/articles.dart'; diff --git a/packages/neon/lib/src/apps/news/widgets/articles_view.dart b/packages/neon/lib/src/apps/news/widgets/articles_view.dart index 65dd67d9..81b30700 100644 --- a/packages/neon/lib/src/apps/news/widgets/articles_view.dart +++ b/packages/neon/lib/src/apps/news/widgets/articles_view.dart @@ -45,26 +45,34 @@ class _NewsArticlesViewState extends State { ) => Scaffold( resizeToAvoidBottomInset: false, - body: RefreshIndicator( - onRefresh: () async { - widget.bloc.refresh(); - }, - child: Column( - children: [ - ExceptionWidget( - articlesError ?? feedsError, - onRetry: () { - if (articlesError != null) { - widget.bloc.refresh(); - } - if (feedsError != null) { - widget.bloc.refreshNewsBloc(); - } - }, - ), - CustomLinearProgressIndicator( - visible: articlesLoading || feedsLoading, - ), + body: SortBoxBuilder( + sortBox: articlesSortBox, + sortPropertyOption: widget.bloc.newsBloc.options.articlesSortPropertyOption, + sortBoxOrderOption: widget.bloc.newsBloc.options.articlesSortBoxOrderOption, + input: articlesData, + builder: (final context, final sorted) => CustomListView( + scrollKey: 'news-articles', + items: feedsData == null ? null : sorted, + isLoading: articlesLoading || feedsLoading, + error: articlesError ?? feedsError, + onRetry: () { + if (articlesError != null) { + widget.bloc.refresh(); + } + if (feedsError != null) { + widget.bloc.refreshNewsBloc(); + } + }, + onRefresh: () async { + widget.bloc.refresh(); + }, + builder: (final context, final article) => _buildArticle( + context, + widget.bloc, + article, + feedsData!.singleWhere((final feed) => feed.id == article.feedId), + ), + topFixedChildren: [ RxBlocBuilder( bloc: widget.bloc, state: (final bloc) => bloc.filterType, @@ -112,33 +120,7 @@ class _NewsArticlesViewState extends State { ), ), ), - if (articlesData != null && feedsData != null) ...[ - Expanded( - child: SortBoxBuilder( - sortBox: articlesSortBox, - sortPropertyOption: widget.bloc.newsBloc.options.articlesSortPropertyOption, - sortBoxOrderOption: widget.bloc.newsBloc.options.articlesSortBoxOrderOption, - input: articlesData, - builder: (final context, final sorted) => CustomListView( - scrollKey: 'news-articles', - items: sorted, - builder: (final context, final article) => _buildArticle( - context, - widget.bloc, - article, - feedsData.singleWhere((final feed) => feed.id == article.feedId), - ), - ), - ), - ), - ], - ] - .intersperse( - const SizedBox( - height: 10, - ), - ) - .toList(), + ], ), ), ), diff --git a/packages/neon/lib/src/apps/news/widgets/feeds_view.dart b/packages/neon/lib/src/apps/news/widgets/feeds_view.dart index b94c90a6..40381898 100644 --- a/packages/neon/lib/src/apps/news/widgets/feeds_view.dart +++ b/packages/neon/lib/src/apps/news/widgets/feeds_view.dart @@ -48,52 +48,34 @@ class NewsFeedsView extends StatelessWidget { }, child: const Icon(Icons.add), ), - body: RefreshIndicator( - onRefresh: () async { - bloc.refresh( - mainArticlesToo: true, - ); - }, - child: Column( - children: [ - ExceptionWidget( - feedsError ?? foldersError, - onRetry: () { - bloc.refresh( - mainArticlesToo: false, - ); - }, - ), - CustomLinearProgressIndicator( - visible: feedsLoading || foldersLoading, - ), - if (feedsData != null && foldersData != null) ...[ - Expanded( - child: SortBoxBuilder( - sortBox: feedsSortBox, - sortPropertyOption: bloc.options.feedsSortPropertyOption, - sortBoxOrderOption: bloc.options.feedsSortBoxOrderOption, - input: feedsData.where((final f) => folderID == null || f.folderId == folderID).toList(), - builder: (final context, final sorted) => CustomListView( - scrollKey: 'news-feeds', - withFloatingActionButton: true, - items: sorted, - builder: (final context, final feed) => _buildFeed( - context, - feed, - foldersData, - ), - ), - ), - ), - ], - ] - .intersperse( - const SizedBox( - height: 10, - ), - ) - .toList(), + body: SortBoxBuilder( + sortBox: feedsSortBox, + sortPropertyOption: bloc.options.feedsSortPropertyOption, + sortBoxOrderOption: bloc.options.feedsSortBoxOrderOption, + input: foldersData == null + ? null + : feedsData?.where((final f) => folderID == null || f.folderId == folderID).toList(), + builder: (final context, final sorted) => CustomListView( + scrollKey: 'news-feeds', + withFloatingActionButton: true, + items: sorted, + isLoading: feedsLoading || foldersLoading, + error: feedsError ?? foldersError, + onRetry: () { + bloc.refresh( + mainArticlesToo: false, + ); + }, + onRefresh: () async { + bloc.refresh( + mainArticlesToo: true, + ); + }, + builder: (final context, final feed) => _buildFeed( + context, + feed, + foldersData!, + ), ), ), ), diff --git a/packages/neon/lib/src/apps/news/widgets/folders_view.dart b/packages/neon/lib/src/apps/news/widgets/folders_view.dart index 6926a642..838024e3 100644 --- a/packages/neon/lib/src/apps/news/widgets/folders_view.dart +++ b/packages/neon/lib/src/apps/news/widgets/folders_view.dart @@ -43,55 +43,37 @@ class NewsFoldersView extends StatelessWidget { final feedsLoading, final _, ) => - RefreshIndicator( - onRefresh: () async { - bloc.refresh( - mainArticlesToo: true, - ); - }, - child: Column( - children: [ - ExceptionWidget( - feedsError ?? foldersError, - onRetry: () { - bloc.refresh( - mainArticlesToo: false, - ); - }, - ), - CustomLinearProgressIndicator( - visible: feedsLoading || foldersLoading, - ), - if (feedsData != null && foldersData != null) ...[ - Expanded( - child: SortBoxBuilder( - sortBox: foldersSortBox, - sortPropertyOption: bloc.options.foldersSortPropertyOption, - sortBoxOrderOption: bloc.options.foldersSortBoxOrderOption, - input: foldersData - .map( - (final folder) => FolderFeedsWrapper( - folder, - feedsData.where((final feed) => feed.folderId == folder.id).toList(), - ), - ) - .toList(), - builder: (final context, final sorted) => CustomListView( - scrollKey: 'news-folders', - withFloatingActionButton: true, - items: sorted, - builder: _buildFolder, + SortBoxBuilder( + sortBox: foldersSortBox, + sortPropertyOption: bloc.options.foldersSortPropertyOption, + sortBoxOrderOption: bloc.options.foldersSortBoxOrderOption, + input: feedsData == null + ? null + : foldersData + ?.map( + (final folder) => FolderFeedsWrapper( + folder, + feedsData.where((final feed) => feed.folderId == folder.id).toList(), ), - ), - ), - ], - ] - .intersperse( - const SizedBox( - height: 10, - ), - ) - .toList(), + ) + .toList(), + builder: (final context, final sorted) => CustomListView( + scrollKey: 'news-folders', + withFloatingActionButton: true, + items: sorted, + isLoading: feedsLoading || foldersLoading, + error: feedsError ?? foldersError, + onRetry: () { + bloc.refresh( + mainArticlesToo: false, + ); + }, + onRefresh: () async { + bloc.refresh( + mainArticlesToo: true, + ); + }, + builder: _buildFolder, ), ), ), diff --git a/packages/neon/lib/src/apps/notes/app.dart b/packages/neon/lib/src/apps/notes/app.dart index 0ba8ea07..cb50d3dc 100644 --- a/packages/neon/lib/src/apps/notes/app.dart +++ b/packages/neon/lib/src/apps/notes/app.dart @@ -5,7 +5,6 @@ import 'dart:convert'; import 'package:crypto/crypto.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; -import 'package:intersperse/intersperse.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/apps/notes/blocs/notes.dart'; diff --git a/packages/neon/lib/src/apps/notes/widgets/categories_view.dart b/packages/neon/lib/src/apps/notes/widgets/categories_view.dart index 69e25e48..b4a6c5d5 100644 --- a/packages/neon/lib/src/apps/notes/widgets/categories_view.dart +++ b/packages/neon/lib/src/apps/notes/widgets/categories_view.dart @@ -19,52 +19,32 @@ class NotesCategoriesView extends StatelessWidget { final notesLoading, final _, ) => - RefreshIndicator( - onRefresh: () async { - bloc.refresh(); - }, - child: Column( - children: [ - ExceptionWidget( - notesError, - onRetry: () { - bloc.refresh(); - }, - ), - CustomLinearProgressIndicator( - visible: notesLoading, - ), - if (notesData != null) ...[ - Expanded( - child: SortBoxBuilder( - sortBox: categoriesSortBox, - sortPropertyOption: bloc.options.categoriesSortPropertyOption, - sortBoxOrderOption: bloc.options.categoriesSortBoxOrderOption, - input: notesData - .map((final note) => note.category!) - .toSet() - .map( - (final category) => NoteCategory( - category, - notesData.where((final note) => note.category == category).length, - ), - ) - .toList(), - builder: (final context, final sorted) => CustomListView( - scrollKey: 'notes-categories', - items: sorted, - builder: _buildCategory, - ), - ), + SortBoxBuilder( + sortBox: categoriesSortBox, + sortPropertyOption: bloc.options.categoriesSortPropertyOption, + sortBoxOrderOption: bloc.options.categoriesSortBoxOrderOption, + input: notesData + ?.map((final note) => note.category!) + .toSet() + .map( + (final category) => NoteCategory( + category, + notesData.where((final note) => note.category == category).length, ), - ], - ] - .intersperse( - const SizedBox( - height: 10, - ), - ) - .toList(), + ) + .toList(), + builder: (final context, final sorted) => CustomListView( + scrollKey: 'notes-categories', + items: sorted, + isLoading: notesLoading, + error: notesError, + onRetry: () { + bloc.refresh(); + }, + onRefresh: () async { + bloc.refresh(); + }, + builder: _buildCategory, ), ), ); diff --git a/packages/neon/lib/src/apps/notes/widgets/notes_view.dart b/packages/neon/lib/src/apps/notes/widgets/notes_view.dart index 46640ed3..5c2b3d4f 100644 --- a/packages/neon/lib/src/apps/notes/widgets/notes_view.dart +++ b/packages/neon/lib/src/apps/notes/widgets/notes_view.dart @@ -41,54 +41,41 @@ class NotesView extends StatelessWidget { }, child: const Icon(Icons.add), ), - body: RefreshIndicator( - onRefresh: () async { - bloc.refresh(); - }, - child: Column( - children: [ - ExceptionWidget( - notesError, - onRetry: () { - bloc.refresh(); - }, - ), - CustomLinearProgressIndicator( - visible: notesLoading, - ), - if (notesData != null) ...[ - Expanded( - child: SortBoxBuilder( - sortBox: notesSortBox, - sortPropertyOption: bloc.options.notesSortPropertyOption, - sortBoxOrderOption: bloc.options.notesSortBoxOrderOption, - input: category != null - ? notesData.where((final note) => note.favorite! && note.category == category).toList() - : notesData.where((final note) => note.favorite!).toList(), - builder: (final context, final sortedFavorites) => SortBoxBuilder( - sortBox: notesSortBox, - sortPropertyOption: bloc.options.notesSortPropertyOption, - sortBoxOrderOption: bloc.options.notesSortBoxOrderOption, - input: category != null - ? notesData.where((final note) => !note.favorite! && note.category == category).toList() - : notesData.where((final note) => !note.favorite!).toList(), - builder: (final context, final sortedNonFavorites) => CustomListView( - scrollKey: 'notes-notes', - withFloatingActionButton: true, - items: [...sortedFavorites, ...sortedNonFavorites], - builder: _buildNote, - ), - ), - ), - ), + body: SortBoxBuilder( + sortBox: notesSortBox, + sortPropertyOption: bloc.options.notesSortPropertyOption, + sortBoxOrderOption: bloc.options.notesSortBoxOrderOption, + input: category != null + ? notesData?.where((final note) => note.favorite! && note.category == category).toList() + : notesData?.where((final note) => note.favorite!).toList(), + builder: (final context, final sortedFavorites) => SortBoxBuilder( + sortBox: notesSortBox, + sortPropertyOption: bloc.options.notesSortPropertyOption, + sortBoxOrderOption: bloc.options.notesSortBoxOrderOption, + input: category != null + ? notesData?.where((final note) => !note.favorite! && note.category == category).toList() + : notesData?.where((final note) => !note.favorite!).toList(), + builder: (final context, final sortedNonFavorites) => CustomListView( + scrollKey: 'notes-notes', + withFloatingActionButton: true, + items: [ + if (sortedFavorites != null) ...[ + ...sortedFavorites, + ], + if (sortedNonFavorites != null) ...[ + ...sortedNonFavorites, + ], ], - ] - .intersperse( - const SizedBox( - height: 10, - ), - ) - .toList(), + isLoading: notesLoading, + error: notesError, + onRetry: () { + bloc.refresh(); + }, + onRefresh: () async { + bloc.refresh(); + }, + builder: _buildNote, + ), ), ), ), diff --git a/packages/neon/lib/src/apps/notifications/app.dart b/packages/neon/lib/src/apps/notifications/app.dart index c2fcb66d..6de988d1 100644 --- a/packages/neon/lib/src/apps/notifications/app.dart +++ b/packages/neon/lib/src/apps/notifications/app.dart @@ -1,7 +1,6 @@ library notifications; import 'package:flutter/material.dart'; -import 'package:intersperse/intersperse.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/apps/notifications/blocs/notifications.dart'; diff --git a/packages/neon/lib/src/apps/notifications/pages/main.dart b/packages/neon/lib/src/apps/notifications/pages/main.dart index 583e39b8..e7785286 100644 --- a/packages/neon/lib/src/apps/notifications/pages/main.dart +++ b/packages/neon/lib/src/apps/notifications/pages/main.dart @@ -42,39 +42,19 @@ class _NotificationsMainPageState extends State { }, child: const Icon(MdiIcons.checkAll), ), - body: RefreshIndicator( + body: CustomListView( + scrollKey: 'notifications-notifications', + withFloatingActionButton: true, + items: notificationsData, + isLoading: notificationsLoading, + error: notificationsError, + onRetry: () { + widget.bloc.refresh(); + }, onRefresh: () async { widget.bloc.refresh(); }, - child: Column( - children: [ - ExceptionWidget( - notificationsError, - onRetry: () { - widget.bloc.refresh(); - }, - ), - CustomLinearProgressIndicator( - visible: notificationsLoading, - ), - if (notificationsData != null) ...[ - Expanded( - child: CustomListView( - scrollKey: 'notifications-notifications', - withFloatingActionButton: true, - items: notificationsData, - builder: _buildNotification, - ), - ), - ], - ] - .intersperse( - const SizedBox( - height: 10, - ), - ) - .toList(), - ), + builder: _buildNotification, ), ), ); diff --git a/packages/neon/lib/src/utils/sort_box_builder.dart b/packages/neon/lib/src/utils/sort_box_builder.dart index b5f9eb16..6ee7d063 100644 --- a/packages/neon/lib/src/utils/sort_box_builder.dart +++ b/packages/neon/lib/src/utils/sort_box_builder.dart @@ -13,8 +13,8 @@ class SortBoxBuilder extends StatelessWidget { final SortBox sortBox; final SelectOption sortPropertyOption; final SelectOption sortBoxOrderOption; - final List input; - final Widget Function(BuildContext, List) builder; + final List? input; + final Widget Function(BuildContext, List?) builder; @override Widget build(final BuildContext context) => OptionBuilder( @@ -25,7 +25,7 @@ class SortBoxBuilder extends StatelessWidget { ? Container() : builder( context, - sortBox.sort(input, Box(property, order)), + input == null ? null : sortBox.sort(input!, Box(property, order)), ), ), ); diff --git a/packages/neon/lib/src/widgets/custom_listview.dart b/packages/neon/lib/src/widgets/custom_listview.dart index 59d0e054..5b591f39 100644 --- a/packages/neon/lib/src/widgets/custom_listview.dart +++ b/packages/neon/lib/src/widgets/custom_listview.dart @@ -3,27 +3,67 @@ part of '../neon.dart'; class CustomListView extends StatelessWidget { const CustomListView({ required this.items, + required this.isLoading, + required this.error, + required this.onRetry, + required this.onRefresh, required this.builder, this.scrollKey, this.withFloatingActionButton = false, + this.topFixedChildren, + this.topScrollingChildren, super.key, }); - final List items; + final List? items; + final bool isLoading; + final dynamic error; + final Function() onRetry; + final Future Function() onRefresh; final Widget Function(BuildContext, T data) builder; final String? scrollKey; final bool withFloatingActionButton; + final List? topFixedChildren; + final List? topScrollingChildren; @override - Widget build(final BuildContext context) => Scrollbar( - child: ListView.separated( - key: scrollKey != null ? PageStorageKey(scrollKey!) : null, - padding: withFloatingActionButton ? const EdgeInsets.only(bottom: 88) : null, - separatorBuilder: (final context, final index) => const SizedBox( - height: 10, - ), - itemCount: items.length, - itemBuilder: (final context, final index) => builder(context, items[index]), + Widget build(final BuildContext context) => RefreshIndicator( + onRefresh: onRefresh, + child: Column( + children: [ + if (topFixedChildren != null) ...[ + ...topFixedChildren!, + ], + CustomLinearProgressIndicator( + margin: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), + visible: isLoading, + ), + Expanded( + child: Scrollbar( + child: ListView( + key: scrollKey != null ? PageStorageKey(scrollKey!) : null, + padding: withFloatingActionButton ? const EdgeInsets.only(bottom: 88) : null, + children: [ + if (topScrollingChildren != null) ...[ + ...topScrollingChildren!, + ], + ExceptionWidget( + error, + onRetry: onRetry, + ), + if (items != null) ...[ + for (final item in items!) ...[ + builder(context, item), + ], + ], + ], + ), + ), + ), + ], ), ); }