From 40546f48e67d23cd6ab5e62cd8a7ee4791b12692 Mon Sep 17 00:00:00 2001 From: zarzet <42882290+zarzet@users.noreply.github.com> Date: Mon, 7 Sep 2026 00:47:15 +0700 Subject: [PATCH] fix(download): allow dragging the quality picker to dismiss --- lib/widgets/download_service_picker.dart | 23 +++--- test/download_service_picker_drag_test.dart | 85 +++++++++++++++++++++ 2 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 test/download_service_picker_drag_test.dart diff --git a/lib/widgets/download_service_picker.dart b/lib/widgets/download_service_picker.dart index bbc96963..71295069 100644 --- a/lib/widgets/download_service_picker.dart +++ b/lib/widgets/download_service_picker.dart @@ -13,6 +13,7 @@ class DownloadServicePicker extends ConsumerStatefulWidget { final String? coverUrl; final void Function(String quality, String service) onSelect; final String? recommendedService; + final ScrollController? scrollController; const DownloadServicePicker({ super.key, @@ -21,6 +22,7 @@ class DownloadServicePicker extends ConsumerStatefulWidget { this.coverUrl, required this.onSelect, this.recommendedService, + this.scrollController, }); @override @@ -42,15 +44,15 @@ class DownloadServicePicker extends ConsumerStatefulWidget { useRootNavigator: true, backgroundColor: colorScheme.surfaceContainerHigh, isScrollControlled: true, - constraints: BoxConstraints( - maxHeight: MediaQuery.sizeOf(context).height * 0.88, - ), - builder: (context) => DownloadServicePicker( - trackName: trackName, - artistName: artistName, - coverUrl: coverUrl, - onSelect: onSelect, - recommendedService: recommendedService, + builder: (context) => AppDraggableSheet( + builder: (context, scrollController) => DownloadServicePicker( + trackName: trackName, + artistName: artistName, + coverUrl: coverUrl, + onSelect: onSelect, + recommendedService: recommendedService, + scrollController: scrollController, + ), ), ); } @@ -122,7 +124,10 @@ class _DownloadServicePickerState extends ConsumerState { final qualityOptions = _getQualityOptions(downloadExtensions); return SafeArea( + top: false, child: SingleChildScrollView( + controller: widget.scrollController, + physics: const ClampingScrollPhysics(), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/test/download_service_picker_drag_test.dart b/test/download_service_picker_drag_test.dart new file mode 100644 index 00000000..fb989666 --- /dev/null +++ b/test/download_service_picker_drag_test.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:spotiflac_android/l10n/app_localizations.dart'; +import 'package:spotiflac_android/models/settings.dart'; +import 'package:spotiflac_android/providers/extension_provider.dart'; +import 'package:spotiflac_android/providers/settings_provider.dart'; +import 'package:spotiflac_android/widgets/download_service_picker.dart'; + +class _Extensions extends ExtensionNotifier { + @override + ExtensionState build() => ExtensionState( + extensions: [ + Extension( + id: 'example', + name: 'example', + displayName: 'Example', + version: '1.0.0', + description: '', + enabled: true, + status: 'loaded', + hasDownloadProvider: true, + qualityOptions: List.generate( + 16, + (index) => QualityOption(id: '$index', label: 'Quality $index'), + ), + ), + ], + ); + + @override + void refreshEnabledExtensionHealth({bool force = false}) {} +} + +class _Settings extends SettingsNotifier { + @override + AppSettings build() => const AppSettings(); +} + +void main() { + testWidgets('iOS picker scrolls options and dismisses when pulled down', ( + tester, + ) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + extensionProvider.overrideWith(_Extensions.new), + settingsProvider.overrideWith(_Settings.new), + ], + child: MaterialApp( + theme: ThemeData(platform: TargetPlatform.iOS), + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + home: Scaffold( + body: Builder( + builder: (context) => TextButton( + onPressed: () => DownloadServicePicker.show( + context, + trackName: 'Example track', + onSelect: (_, _) {}, + ), + child: const Text('Open'), + ), + ), + ), + ), + ), + ); + await tester.tap(find.text('Open')); + await tester.pumpAndSettle(); + final scroll = find.descendant( + of: find.byType(DownloadServicePicker), + matching: find.byType(SingleChildScrollView), + ); + await tester.drag(scroll, const Offset(0, -300)); + await tester.pumpAndSettle(); + expect(find.byType(DownloadServicePicker), findsOneWidget); + await tester.drag(scroll, const Offset(0, 1500)); + await tester.pumpAndSettle(); + await tester.drag(find.text('Example track'), const Offset(0, 600)); + await tester.pumpAndSettle(); + expect(find.byType(DownloadServicePicker), findsNothing); + expect(tester.takeException(), isNull); + }); +}