diff --git a/example/pubspec.lock b/example/pubspec.lock index 5365258..93deea0 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -127,26 +127,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -259,10 +259,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.6" typed_data: dependency: transitive description: @@ -299,10 +299,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: @@ -336,5 +336,5 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.7.0-0 <4.0.0" + dart: ">=3.9.0 <4.0.0" flutter: ">=3.27.0" diff --git a/lib/src/presentation/di/injector.dart b/lib/src/presentation/di/injector.dart index 1ac4000..abfde1f 100644 --- a/lib/src/presentation/di/injector.dart +++ b/lib/src/presentation/di/injector.dart @@ -9,6 +9,7 @@ import 'package:suggest_a_feature/src/presentation/localization/localization_ext import 'package:suggest_a_feature/src/presentation/localization/localization_options.dart'; import 'package:suggest_a_feature/src/presentation/pages/theme/suggestions_theme.dart'; +// Singleton accessor pattern - private implementation is intentional // ignore: library_private_types_in_public_api _Injector get i => _Injector(); diff --git a/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_bottom_sheet.dart b/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_bottom_sheet.dart index 728e11f..cc093bd 100644 --- a/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_bottom_sheet.dart +++ b/lib/src/presentation/pages/suggestion/create_edit/create_edit_suggestion_bottom_sheet.dart @@ -89,7 +89,10 @@ class _CreateEditSuggestionBottomSheetState previous.suggestion.status != current.suggestion.status; } - void _listener(BuildContext context, CreateEditSuggestionState state) { + Future _listener( + BuildContext context, + CreateEditSuggestionState state, + ) async { final stateManager = CreateEditSuggestionManager.of(context); if (state.savingImageResultMessageType != SavingResultMessageType.none) { ScaffoldMessenger.of(context).showSnackBar( @@ -105,7 +108,7 @@ class _CreateEditSuggestionBottomSheetState } else if (state.isSubmitted) { widget.onClose(); } else if (state.isPhotoViewOpen) { - _openPhotoView(state, stateManager); + await _openPhotoView(state, stateManager); } stateManager.reset(); } @@ -400,11 +403,11 @@ class _PhotoPickerItem extends StatelessWidget { tileWidth: state.suggestion.images.length > 2 ? tileWidth * 0.9 : tileWidth, - onUploadPhotos: () { + onUploadPhotos: () async { final availableNumOfPhotos = maxPhotosForOneSuggestion - state.suggestion.images.length; if (availableNumOfPhotos > 0) { - stateManager.addUploadedPhotos( + await stateManager.addUploadedPhotos( onUploadMultiplePhotos!( availableNumOfPhotos: availableNumOfPhotos, ), diff --git a/lib/src/presentation/pages/suggestion/suggestion_page.dart b/lib/src/presentation/pages/suggestion/suggestion_page.dart index 3b4dd0a..87e9a2b 100644 --- a/lib/src/presentation/pages/suggestion/suggestion_page.dart +++ b/lib/src/presentation/pages/suggestion/suggestion_page.dart @@ -272,9 +272,7 @@ class _UserInfo extends StatelessWidget { ), if (onShareSuggestion != null) IconButton( - onPressed: () { - onShareSuggestion!(suggestionId); - }, + onPressed: () => onShareSuggestion!(suggestionId), icon: const Icon(Icons.share), ), ], @@ -500,26 +498,24 @@ class _WrappedAttachedImage extends StatelessWidget { Widget build(BuildContext context) { final suggestionManager = SuggestionManager.of(context); return GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - barrierColor: Colors.black, - context: context, - useRootNavigator: false, - builder: (_) { - return PhotoView( - onDownloadClick: onSaveToGallery != null - ? (path) => suggestionManager.showSavingResultMessage( - onSaveToGallery!(path), - ) - : null, - initialIndex: images.indexOf(attachedImage), - photos: images, - previousNavBarColor: context.theme.colorScheme.surface, - ); - }, - ); - }, + onTap: () => showDialog( + useSafeArea: false, + barrierColor: Colors.black, + context: context, + useRootNavigator: false, + builder: (_) { + return PhotoView( + onDownloadClick: onSaveToGallery != null + ? (path) => suggestionManager.showSavingResultMessage( + onSaveToGallery!(path), + ) + : null, + initialIndex: images.indexOf(attachedImage), + photos: images, + previousNavBarColor: context.theme.colorScheme.surface, + ); + }, + ), child: Container( width: (MediaQuery.of(context).size.width - 80) / 3, height: (MediaQuery.of(context).size.width - 80) / 3, @@ -627,10 +623,9 @@ class _OpenConfirmationBottomSheet extends StatelessWidget { return ConfirmationBottomSheet( controller: sheetController, question: localization.deletionQuestion, - onConfirm: () { - stateManager - ..closeBottomSheet() - ..deleteSuggestion(); + onConfirm: () async { + stateManager.closeBottomSheet(); + await stateManager.deleteSuggestion(); }, onCancel: ([_]) async { await sheetController.collapse(); @@ -653,10 +648,9 @@ class _OpenCommentConfirmationBottomSheet extends StatelessWidget { return ConfirmationBottomSheet( controller: sheetController, question: localization.deletionCommentQuestion, - onConfirm: () { - stateManager - ..closeBottomSheet() - ..deleteComment(); + onConfirm: () async { + stateManager.closeBottomSheet(); + await stateManager.deleteComment(); }, onCancel: ([_]) async { await sheetController.collapse(); @@ -768,8 +762,8 @@ class _OpenCreateCommentBottomSheet extends StatelessWidget { String text, { required bool isAnonymous, required bool postedByAdmin, - }) { - stateManager.createComment( + }) async { + await stateManager.createComment( text, onGetUserById, isAnonymous: isAnonymous, diff --git a/lib/src/presentation/pages/suggestion/suggestion_state_manager.dart b/lib/src/presentation/pages/suggestion/suggestion_state_manager.dart index a3bd8b7..6799645 100644 --- a/lib/src/presentation/pages/suggestion/suggestion_state_manager.dart +++ b/lib/src/presentation/pages/suggestion/suggestion_state_manager.dart @@ -50,25 +50,27 @@ class SuggestionStateManager extends State { suggestion: Suggestion.empty(), loadingComments: true, ); - _init( - suggestion: widget.suggestion, - getUserById: widget.onGetUserById, - isAdmin: i.isAdmin, + unawaited( + _init( + suggestion: widget.suggestion, + getUserById: widget.onGetUserById, + isAdmin: i.isAdmin, + ), ); } @override void dispose() { - _suggestionSubscription?.cancel(); + unawaited(_suggestionSubscription?.cancel()); _suggestionSubscription = null; super.dispose(); } - void _init({ + Future _init({ required Suggestion suggestion, required OnGetUserById getUserById, required bool isAdmin, - }) { + }) async { _update( state.newState( suggestion: suggestion, @@ -77,7 +79,7 @@ class SuggestionStateManager extends State { isAdmin, ), ); - _suggestionSubscription?.cancel(); + await _suggestionSubscription?.cancel(); _suggestionSubscription = _suggestionRepository.suggestionsStream.listen(_onNewSuggestions); _loadComments(getUserById, suggestion.id); @@ -137,7 +139,7 @@ class SuggestionStateManager extends State { state.suggestion, saveComments: false, ); - } catch (e) { + } on Exception catch (e) { log('Comments loading error', error: e); } _update( @@ -286,7 +288,7 @@ class SuggestionStateManager extends State { state.suggestion, saveComments: false, ); - } catch (e) { + } on Exception catch (e) { log('Comment creation error', error: e); } } diff --git a/lib/src/presentation/pages/suggestions/suggestions_state_manager.dart b/lib/src/presentation/pages/suggestions/suggestions_state_manager.dart index 1cb96ca..ad4d35e 100644 --- a/lib/src/presentation/pages/suggestions/suggestions_state_manager.dart +++ b/lib/src/presentation/pages/suggestions/suggestions_state_manager.dart @@ -49,11 +49,11 @@ class SuggestionsStateManager extends State { sortType: widget.sortType, loading: true, ); - _init(); + unawaited(_init()); } Future _init() async { - _suggestionSubscription?.cancel(); + await _suggestionSubscription?.cancel(); _suggestionSubscription = _suggestionRepository.suggestionsStream.listen( _onNewSuggestions, ); @@ -74,7 +74,7 @@ class SuggestionsStateManager extends State { loading: state.loading, suggestion: suggestion, ); - } catch (e) { + } on Exception catch (e) { if (kDebugMode) { log('Redirect to suggestion error', error: e); } @@ -86,7 +86,7 @@ class SuggestionsStateManager extends State { @override void dispose() { - _suggestionSubscription?.cancel(); + unawaited(_suggestionSubscription?.cancel()); _suggestionSubscription = null; super.dispose(); } @@ -187,10 +187,10 @@ class SuggestionsStateManager extends State { ), ); - void onSortTypeChanged(SortType sortType) { + Future onSortTypeChanged(SortType sortType) async { if (sortType != state.sortType) { _update(state.newState(sortType: sortType)); - _onNewSuggestions(_suggestionRepository.suggestions); + await _onNewSuggestions(_suggestionRepository.suggestions); } } diff --git a/lib/src/presentation/pages/widgets/bottom_sheets/create_comment_bottom_sheet.dart b/lib/src/presentation/pages/widgets/bottom_sheets/create_comment_bottom_sheet.dart index 8f9c626..1f2ed4d 100644 --- a/lib/src/presentation/pages/widgets/bottom_sheets/create_comment_bottom_sheet.dart +++ b/lib/src/presentation/pages/widgets/bottom_sheets/create_comment_bottom_sheet.dart @@ -65,7 +65,7 @@ class _CreateCommentBottomSheetState extends State { context.theme.colorScheme.surface, previousNavBarColor: context.theme.colorScheme.surface, previousStatusBarColor: context.theme.colorScheme.surface, - contentBuilder: (_, __) { + contentBuilder: (_, _) { return ListView( padding: const EdgeInsets.only( top: Dimensions.marginSmall, diff --git a/lib/src/presentation/pages/widgets/bottom_sheets/label_bottom_sheet.dart b/lib/src/presentation/pages/widgets/bottom_sheets/label_bottom_sheet.dart index f0789a7..aa1d5f5 100644 --- a/lib/src/presentation/pages/widgets/bottom_sheets/label_bottom_sheet.dart +++ b/lib/src/presentation/pages/widgets/bottom_sheets/label_bottom_sheet.dart @@ -56,7 +56,7 @@ class _LabelBottomSheetState extends State { }, backgroundColor: context.theme.bottomSheetTheme.backgroundColor ?? context.theme.colorScheme.surface, - contentBuilder: (_, __) { + contentBuilder: (_, _) { return _LabelsListView( onTap: (label) => setState( () => selectedLabels.contains(label) diff --git a/lib/src/presentation/pages/widgets/network_image.dart b/lib/src/presentation/pages/widgets/network_image.dart index 7fb5351..9118ecb 100644 --- a/lib/src/presentation/pages/widgets/network_image.dart +++ b/lib/src/presentation/pages/widgets/network_image.dart @@ -38,7 +38,7 @@ class SuggestionsNetworkImage extends StatelessWidget { color: noImageColor ?? context.theme.colorScheme.surface, ); }, - errorBuilder: (_, __, ___) => ColoredBox( + errorBuilder: (_, _, _) => ColoredBox( color: noImageColor ?? context.theme.colorScheme.surface, ), ), diff --git a/lib/src/presentation/pages/widgets/photo_view.dart b/lib/src/presentation/pages/widgets/photo_view.dart index 2d6e222..d8984fd 100644 --- a/lib/src/presentation/pages/widgets/photo_view.dart +++ b/lib/src/presentation/pages/widgets/photo_view.dart @@ -30,7 +30,7 @@ class _PhotoViewState extends State { late PageController _galleryPageController; late int _currentIndex; final Map _touchPositions = {}; - ScrollPhysics _scrollPhysics = const _CustomPageViewScrollPhysics(); + ScrollPhysics _scrollPhysics = const PageScrollPhysics(); var _isZoomed = false; @@ -105,7 +105,7 @@ class _PhotoViewState extends State { setState(() { _scrollPhysics = _touchPositions.length > 1 || _isZoomed ? const NeverScrollableScrollPhysics() - : const _CustomPageViewScrollPhysics(); + : const PageScrollPhysics(); }); } @@ -180,18 +180,3 @@ class _ActionButtons extends StatelessWidget { ); } } - -class _CustomPageViewScrollPhysics extends ScrollPhysics { - const _CustomPageViewScrollPhysics({super.parent}); - - @override - _CustomPageViewScrollPhysics applyTo(ScrollPhysics? ancestor) => - _CustomPageViewScrollPhysics(parent: buildParent(ancestor)); - - @override - SpringDescription get spring => const SpringDescription( - mass: 50, - stiffness: 100, - damping: 0.8, - ); -} diff --git a/lib/src/presentation/pages/widgets/zoomable_image.dart b/lib/src/presentation/pages/widgets/zoomable_image.dart index 5fadb70..4e0cc63 100644 --- a/lib/src/presentation/pages/widgets/zoomable_image.dart +++ b/lib/src/presentation/pages/widgets/zoomable_image.dart @@ -62,7 +62,7 @@ class _ZoomableImageState extends State ); } - void _onDoubleTap(TapDownDetails details) { + Future _onDoubleTap(TapDownDetails details) async { _animationController.addListener(_animationListener); widget.changeZoomStatus( @@ -74,20 +74,21 @@ class _ZoomableImageState extends State final x = -pos.dx * (_doubleTapZoomScale - 1); final y = -pos.dy * (_doubleTapZoomScale - 1); final zoomedMatrix = Matrix4.identity() - ..translate(x, y) - ..scale(_doubleTapZoomScale); + ..translateByDouble(x, y, 1, 1) + ..scaleByDouble(_doubleTapZoomScale, _doubleTapZoomScale, 1, 1); final endMatrix = _transformationController.value.isIdentity() ? zoomedMatrix : Matrix4.identity(); - _animation = Matrix4Tween( - begin: _transformationController.value, - end: endMatrix, - ).animate( - CurveTween(curve: Curves.easeInOut).animate(_animationController), - ); - _animationController.forward(from: 0).then( + _animation = + Matrix4Tween( + begin: _transformationController.value, + end: endMatrix, + ).animate( + CurveTween(curve: Curves.easeInOut).animate(_animationController), + ); + await _animationController.forward(from: 0).then( (_) { _animationController.removeListener( _animationListener, diff --git a/lib/suggest_a_feature.dart b/lib/suggest_a_feature.dart index 69ac314..9f91f2e 100644 --- a/lib/suggest_a_feature.dart +++ b/lib/suggest_a_feature.dart @@ -4,7 +4,7 @@ /// to implement additional menu in their own mobile app where users can share /// their suggestions about the application in real time, discuss them with /// others, and vote for each other's suggestions. -library suggest_a_feature; +library; export 'src/data/interfaces/suggestions_data_source.dart'; export 'src/domain/entities/admin_settings.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index e44ee87..943837b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -10,22 +10,23 @@ topics: - feedback environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.9.0 <4.0.0" flutter: ">=3.10.0" dependencies: - equatable: ^2.0.5 + equatable: ^2.0.7 flutter: sdk: flutter flutter_svg: ^2.0.9 intl: ^0.20.2 - wtf_sliding_sheet: ^1.1.3 + wtf_sliding_sheet: ^1.1.5 dev_dependencies: - build_runner: ^2.4.7 + build_runner: ^2.9.0 flutter_test: sdk: flutter - very_good_analysis: ^5.1.0 + vector_graphics_compiler: ^1.1.19 + very_good_analysis: ^9.0.0 flutter: assets: