From 4dad60d937022b8ae4085b3e3470dcf58b74629f Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Tue, 12 Mar 2024 01:55:28 +0100 Subject: [PATCH 01/25] Test that correct pages are displayed for navRailIndexStateProvider value --- pubspec.lock | 8 +++ pubspec.yaml | 7 +- test/providers/ui_providers_test.dart | 93 +++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 test/providers/ui_providers_test.dart diff --git a/pubspec.lock b/pubspec.lock index f48a9105..38a05b26 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -729,6 +729,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + mockito: + dependency: "direct main" + description: + name: mockito + sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" + url: "https://pub.dev" + source: hosted + version: "5.4.4" mpv_dart: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8441a0f5..b4137063 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,11 @@ name: apidash description: API Dash is a beautiful open-source cross-platform API Client built using Flutter which can help you easily create & customize your API requests, visually inspect responses and generate Dart code on the go. -publish_to: "none" +publish_to: 'none' version: 0.3.0+3 environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=3.7.2" + sdk: '>=3.0.0 <4.0.0' + flutter: '>=3.7.2' dependencies: flutter: @@ -55,6 +55,7 @@ dependencies: dart_style: ^2.3.4 json_text_field: ^1.1.0 csv: ^5.1.1 + mockito: ^5.4.4 dev_dependencies: flutter_test: diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart new file mode 100644 index 00000000..7b32c60a --- /dev/null +++ b/test/providers/ui_providers_test.dart @@ -0,0 +1,93 @@ +import 'dart:io'; + +import 'package:apidash/providers/providers.dart'; +import 'package:apidash/screens/dashboard.dart'; +import 'package:apidash/screens/home_page/home_page.dart'; +import 'package:apidash/screens/intro_page.dart'; +import 'package:apidash/screens/settings_page.dart'; +import 'package:apidash/services/hive_services.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; + +class MockPathProviderPlatform extends Mock implements MethodChannel {} + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() async { + const MethodChannel channel = + MethodChannel('plugins.flutter.io/path_provider'); + final MockPathProviderPlatform mock = MockPathProviderPlatform(); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + if (methodCall.method == 'getApplicationDocumentsDirectory') { + // Create a mock app doc directory for testing + Directory tempDir = + await Directory.systemTemp.createTemp('mock_app_doc_dir'); + return tempDir.path; // Return the path to the mock directory + } + return null; + }); + await openBoxes(); + }); + + group('Testing navRailIndexStateProvider', () { + testWidgets('Dashboard should display correct initial page', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Verify that the HomePage is displayed initially + expect(find.byType(HomePage), findsOneWidget); + expect(find.byType(IntroPage), findsNothing); + expect(find.byType(SettingsPage), findsNothing); + }); + + testWidgets( + "Dashboard should display IntroPage when navRailIndexStateProvider is 1", + (WidgetTester tester) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + navRailIndexStateProvider.overrideWith((ref) => 1), + ], + child: const MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Verify that the IntroPage is displayed + expect(find.byType(IntroPage), findsOneWidget); + expect(find.byType(HomePage), findsNothing); + expect(find.byType(SettingsPage), findsNothing); + }); + testWidgets( + "Dashboard should display SettingsPage when navRailIndexStateProvider is 2", + (WidgetTester tester) async { + await tester.pumpWidget( + ProviderScope( + overrides: [ + navRailIndexStateProvider.overrideWith((ref) => 2), + ], + child: const MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Verify that the SettingsPage is displayed + expect(find.byType(SettingsPage), findsOneWidget); + expect(find.byType(IntroPage), findsNothing); + expect(find.byType(HomePage), findsNothing); + }); + }); +} From b264a7e6a4873a74ba8cf60a15b35fd60b34d52e Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Tue, 12 Mar 2024 02:13:11 +0100 Subject: [PATCH 02/25] Ensure that navigation rail index update when icon button is pressed --- test/providers/ui_providers_test.dart | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index 7b32c60a..4b632113 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -89,5 +89,26 @@ void main() { expect(find.byType(IntroPage), findsNothing); expect(find.byType(HomePage), findsNothing); }); + + testWidgets( + 'Navigation Rail index should update when icon button is pressed', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Tap on the Intro icon + await tester.tap(find.byIcon(Icons.help_outline)); + await tester.pump(); + + // Verify that the navRailIndexStateProvider is updated + final dashboard = tester.element(find.byType(Dashboard)); + final container = ProviderScope.containerOf(dashboard); + expect(container.read(navRailIndexStateProvider), 1); + }); }); } From ba4bd77795356a71b27138d2d2e8b30ceee17ce1 Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Tue, 12 Mar 2024 02:17:22 +0100 Subject: [PATCH 03/25] Test that nav rail index persist across widget rebuilds --- test/providers/ui_providers_test.dart | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index 4b632113..3acf30df 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -110,5 +110,40 @@ void main() { final container = ProviderScope.containerOf(dashboard); expect(container.read(navRailIndexStateProvider), 1); }); + + testWidgets('Navigation Rail index should persist across widget rebuilds', + (tester) async { + // Pump the initial widget tree + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Tap on the Settings icon to change the index to 2 + await tester.tap(find.byIcon(Icons.settings_outlined)); + await tester.pump(); // Wait for the animations to complete + + // Rebuild the widget tree with the same ProviderScope + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Verify that the navRailIndexStateProvider still has the updated value + final dashboard = tester.element(find.byType(Dashboard)); + final container = ProviderScope.containerOf(dashboard); + expect(container.read(navRailIndexStateProvider), 2); + + // Verify that the SettingsPage is still displayed after the rebuild + expect(find.byType(SettingsPage), findsOneWidget); + expect(find.byType(IntroPage), findsNothing); + expect(find.byType(HomePage), findsNothing); + }); }); } From a882d5102264b76ab8d855a495c5b0cb2290e6e3 Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Tue, 12 Mar 2024 02:38:54 +0100 Subject: [PATCH 04/25] Test that UI updates correctly when nav rail index changes --- test/providers/ui_providers_test.dart | 35 ++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index 3acf30df..3ffe1165 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -124,7 +124,7 @@ void main() { // Tap on the Settings icon to change the index to 2 await tester.tap(find.byIcon(Icons.settings_outlined)); - await tester.pump(); // Wait for the animations to complete + await tester.pump(); // Rebuild the widget tree with the same ProviderScope await tester.pumpWidget( @@ -145,5 +145,38 @@ void main() { expect(find.byType(IntroPage), findsNothing); expect(find.byType(HomePage), findsNothing); }); + + testWidgets('UI should update correctly when Navigation Rail index changes', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Grab the Dashboard widget and its ProviderContainer + final dashboard = tester.element(find.byType(Dashboard)); + final container = ProviderScope.containerOf(dashboard); + + // Go to IntroPage + container.read(navRailIndexStateProvider.notifier).state = 1; + await tester.pump(); + + // Verify that the IntroPage is displayed + expect(find.byType(IntroPage), findsOneWidget); + // Verify that the selected icon is the filled version (selectedIcon) + expect(find.byIcon(Icons.help), findsOneWidget); + + // Go to SettingsPage + container.read(navRailIndexStateProvider.notifier).state = 2; + await tester.pump(); + + // Verify that the SettingsPage is displayed + expect(find.byType(SettingsPage), findsOneWidget); + // Verify that the selected icon is the filled version (selectedIcon) + expect(find.byIcon(Icons.settings), findsOneWidget); + }); }); } From 405decbd565a352ff776a53b2677f07113d821df Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Tue, 12 Mar 2024 02:48:54 +0100 Subject: [PATCH 05/25] Test that navRailIndexStateProvider is properly disposed --- test/providers/ui_providers_test.dart | 41 +++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index 3ffe1165..d714998e 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -91,7 +91,7 @@ void main() { }); testWidgets( - 'Navigation Rail index should update when icon button is pressed', + 'navRailIndexStateProvider should update when icon button is pressed', (tester) async { await tester.pumpWidget( const ProviderScope( @@ -111,7 +111,8 @@ void main() { expect(container.read(navRailIndexStateProvider), 1); }); - testWidgets('Navigation Rail index should persist across widget rebuilds', + testWidgets( + 'navRailIndexStateProvider should persist across widget rebuilds', (tester) async { // Pump the initial widget tree await tester.pumpWidget( @@ -146,7 +147,8 @@ void main() { expect(find.byType(HomePage), findsNothing); }); - testWidgets('UI should update correctly when Navigation Rail index changes', + testWidgets( + 'UI should update correctly when navRailIndexStateProvider changes', (tester) async { await tester.pumpWidget( const ProviderScope( @@ -178,5 +180,38 @@ void main() { // Verify that the selected icon is the filled version (selectedIcon) expect(find.byIcon(Icons.settings), findsOneWidget); }); + + testWidgets( + 'navRailIndexStateProvider should be disposed when Dashboard is removed', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: Dashboard(), + ), + ), + ); + + // Grab the Dashboard widget and its ProviderContainer + final dashboard = tester.element(find.byType(Dashboard)); + final container = ProviderScope.containerOf(dashboard); + + // Pumping a different widget to remove the Dashboard from the widget tree + await tester.pumpWidget( + const MaterialApp( + home: Scaffold(body: Text('Different Widget')), + ), + ); + + // Verify that the ProviderContainer has been disposed + // by trying to read from disposed container + bool isDisposed = false; + try { + container.read(navRailIndexStateProvider); + } catch (e) { + isDisposed = true; + } + expect(isDisposed, true); + }); }); } From f71bf00dc3538a6c54bc14a7a2a37fe95e4b9a95 Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Tue, 12 Mar 2024 03:57:51 +0100 Subject: [PATCH 06/25] Remove mockito --- pubspec.lock | 8 -------- pubspec.yaml | 1 - test/providers/ui_providers_test.dart | 5 +---- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 38a05b26..f48a9105 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -729,14 +729,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" - mockito: - dependency: "direct main" - description: - name: mockito - sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917" - url: "https://pub.dev" - source: hosted - version: "5.4.4" mpv_dart: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b4137063..9135b207 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,7 +55,6 @@ dependencies: dart_style: ^2.3.4 json_text_field: ^1.1.0 csv: ^5.1.1 - mockito: ^5.4.4 dev_dependencies: flutter_test: diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index d714998e..f69d8ab3 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -10,9 +10,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; - -class MockPathProviderPlatform extends Mock implements MethodChannel {} void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -20,7 +17,6 @@ void main() { setUp(() async { const MethodChannel channel = MethodChannel('plugins.flutter.io/path_provider'); - final MockPathProviderPlatform mock = MockPathProviderPlatform(); TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, (MethodCall methodCall) async { if (methodCall.method == 'getApplicationDocumentsDirectory') { @@ -70,6 +66,7 @@ void main() { expect(find.byType(HomePage), findsNothing); expect(find.byType(SettingsPage), findsNothing); }); + testWidgets( "Dashboard should display SettingsPage when navRailIndexStateProvider is 2", (WidgetTester tester) async { From a057281a6f69fc8ff46858b97d4a51a2dffdf250 Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Tue, 12 Mar 2024 04:07:09 +0100 Subject: [PATCH 07/25] Test that selectedIdEditStateProvider has initial value of null --- test/providers/ui_providers_test.dart | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index f69d8ab3..cc9b25c0 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:apidash/providers/providers.dart'; import 'package:apidash/screens/dashboard.dart'; +import 'package:apidash/screens/home_page/collection_pane.dart'; import 'package:apidash/screens/home_page/home_page.dart'; import 'package:apidash/screens/intro_page.dart'; import 'package:apidash/screens/settings_page.dart'; @@ -211,4 +212,23 @@ void main() { expect(isDisposed, true); }); }); + + group("Testing selectedIdEditStateProvider", () { + testWidgets( + 'selectedIdEditStateProvider should have an initial value of null', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: CollectionPane(), + ), + ), + ); + + // Verify that the initial value is null + final collectionPane = tester.element(find.byType(CollectionPane)); + final container = ProviderScope.containerOf(collectionPane); + expect(container.read(selectedIdEditStateProvider), null); + }); + }); } From f1ec852acb994b3e12c7a50c49d70b1d7bdcc250 Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Sun, 17 Mar 2024 05:39:54 +0100 Subject: [PATCH 08/25] Add test case for selectedIdEditStateProvider after rename button is tapped --- test/providers/ui_providers_test.dart | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index cc9b25c0..ee62131e 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -230,5 +230,35 @@ void main() { final container = ProviderScope.containerOf(collectionPane); expect(container.read(selectedIdEditStateProvider), null); }); + + testWidgets( + 'selectedIdEditStateProvider should not be null after rename button has been tapped', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: CollectionPane(), + ), + ), + ); + + // Tap on the three dots to open the request card menu + await tester.tap(find.byType(RequestList)); + await tester.pump(); + await tester.tap(find.byType(RequestItem)); + await tester.pump(); + await tester.tap(find.byIcon(Icons.more_vert).first); + await tester.pumpAndSettle(); + + // Tap on the "Rename" option in the menu + await tester.tap(find.text('Rename')); + await tester.pumpAndSettle(); + + // Verify that the selectedIdEditStateProvider is not null + final collectionPane = tester.element(find.byType(CollectionPane)); + final container = ProviderScope.containerOf(collectionPane); + expect(container.read(selectedIdEditStateProvider), isNotNull); + expect((container.read(selectedIdEditStateProvider)).runtimeType, String); + }); }); } From 1513cfe3c1447b72b35be69706c9f3e8c1d5f020 Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Sun, 17 Mar 2024 05:55:39 +0100 Subject: [PATCH 09/25] Add test case for when selectedIdEditStateProvider is reset --- test/providers/ui_providers_test.dart | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index ee62131e..fb9056e1 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -260,5 +260,44 @@ void main() { expect(container.read(selectedIdEditStateProvider), isNotNull); expect((container.read(selectedIdEditStateProvider)).runtimeType, String); }); + + testWidgets( + 'It should be set back to null when user taps outside name editor', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: CollectionPane(), + ), + ), + ); + + // Grab the CollectionPane widget and its ProviderContainer + final collectionPane = tester.element(find.byType(CollectionPane)); + final container = ProviderScope.containerOf(collectionPane); + + // Tap on the three dots to open the request card menu + await tester.tap(find.byType(RequestList)); + await tester.pump(); + await tester.tap(find.byType(RequestItem)); + await tester.pump(); + await tester.tap(find.byIcon(Icons.more_vert).first); + await tester.pumpAndSettle(); + + // Tap on the "Rename" option in the menu + await tester.tap(find.text('Rename')); + await tester.pumpAndSettle(); + + // Verify that the selectedIdEditStateProvider is not null + expect(container.read(selectedIdEditStateProvider), isNotNull); + expect((container.read(selectedIdEditStateProvider)).runtimeType, String); + + // Tap on the screen to simulate tapping outside the name editor + await tester.tap(find.byType(CollectionPane)); + await tester.pumpAndSettle(); + + // Verify that the selectedIdEditStateProvider is null + expect(container.read(selectedIdEditStateProvider), null); + }); }); } From df25a1b1b366293ff67d52c91dfd9888ff85ef54 Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Sun, 17 Mar 2024 06:04:49 +0100 Subject: [PATCH 10/25] Add test case to verify proper disposal of selectedIdEditStateProvider --- test/providers/ui_providers_test.dart | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index fb9056e1..dde4983f 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -299,5 +299,35 @@ void main() { // Verify that the selectedIdEditStateProvider is null expect(container.read(selectedIdEditStateProvider), null); }); + testWidgets("It should be properly disposed", (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: CollectionPane(), + ), + ), + ); + + // Grab the Dashboard widget and its ProviderContainer + final collectionPane = tester.element(find.byType(CollectionPane)); + final container = ProviderScope.containerOf(collectionPane); + + // Pumping a different widget to remove the CollectionPane from the widget tree + await tester.pumpWidget( + const MaterialApp( + home: Scaffold(body: Text('Foo')), + ), + ); + + // Verify that the ProviderContainer has been disposed + // by trying to read from disposed container + bool isDisposed = false; + try { + container.read(selectedIdEditStateProvider); + } catch (e) { + isDisposed = true; + } + expect(isDisposed, true); + }); }); } From de19fc427970db4f8bc283f69cc3de41fc727dcf Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Thu, 21 Mar 2024 00:04:45 +0100 Subject: [PATCH 11/25] Test initial value of sentRequestIdStateProvider --- test/providers/ui_providers_test.dart | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index dde4983f..c7a2f5a0 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -330,4 +330,23 @@ void main() { expect(isDisposed, true); }); }); + + group("Testing sentRequestIdStateProvider", () { + testWidgets( + 'sentRequestIdStateProvider should have an initial value of null', + (tester) async { + await tester.pumpWidget( + const ProviderScope( + child: MaterialApp( + home: CollectionPane(), + ), + ), + ); + + // Verify that the initial value is null + final collectionPane = tester.element(find.byType(CollectionPane)); + final container = ProviderScope.containerOf(collectionPane); + expect(container.read(sentRequestIdStateProvider), null); + }); + }); } From d2aee57ef173e5cf2f90c718f8a2efc674d6fd5c Mon Sep 17 00:00:00 2001 From: Sixtus Agbo Date: Thu, 21 Mar 2024 01:31:55 +0100 Subject: [PATCH 12/25] Remove test case for sentRequestIdStateProvider I removed test case for this provider because this provider does not exist in the repository anymore. With recent changes, it has been removed. --- test/providers/ui_providers_test.dart | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index c7a2f5a0..dde4983f 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -330,23 +330,4 @@ void main() { expect(isDisposed, true); }); }); - - group("Testing sentRequestIdStateProvider", () { - testWidgets( - 'sentRequestIdStateProvider should have an initial value of null', - (tester) async { - await tester.pumpWidget( - const ProviderScope( - child: MaterialApp( - home: CollectionPane(), - ), - ), - ); - - // Verify that the initial value is null - final collectionPane = tester.element(find.byType(CollectionPane)); - final container = ProviderScope.containerOf(collectionPane); - expect(container.read(sentRequestIdStateProvider), null); - }); - }); } From 796b38c8e7b144095eeef7a923fd8e48ead0d562 Mon Sep 17 00:00:00 2001 From: DenserMeerkat Date: Fri, 22 Mar 2024 17:17:51 +0530 Subject: [PATCH 13/25] fix: prevent adding multiple empty rows --- .../request_pane/request_form_data.dart | 14 +++++++++++--- .../details_card/request_pane/request_headers.dart | 8 ++++++-- .../details_card/request_pane/request_params.dart | 8 ++++++-- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart index b3d8f67c..fd00b020 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart @@ -18,6 +18,7 @@ class _FormDataBodyState extends ConsumerState { late int seed; final random = Random.secure(); late List formRows; + bool isAddingRow = false; @override void initState() { @@ -45,6 +46,7 @@ class _FormDataBodyState extends ConsumerState { ] : rF; formRows = isFormDataEmpty ? rows : rows + [kFormDataEmptyModel]; + isAddingRow = false; DaviModel daviModelRows = DaviModel( rows: formRows, @@ -64,7 +66,10 @@ class _FormDataBodyState extends ConsumerState { hintText: " Add Key", onChanged: (value) { formRows[idx] = formRows[idx].copyWith(name: value); - if (isLast) formRows.add(kFormDataEmptyModel); + if (isLast && !isAddingRow) { + isAddingRow = true; + formRows.add(kFormDataEmptyModel); + } _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, @@ -75,7 +80,7 @@ class _FormDataBodyState extends ConsumerState { type: value ?? FormDataType.text, ); formRows[idx] = formRows[idx].copyWith(value: ""); - if (idx == formRows.length - 1 && hasChanged) { + if (isLast && hasChanged) { formRows.add(kFormDataEmptyModel); } setState(() {}); @@ -157,7 +162,10 @@ class _FormDataBodyState extends ConsumerState { hintText: " Add Value", onChanged: (value) { formRows[idx] = formRows[idx].copyWith(value: value); - if (isLast) formRows.add(kFormDataEmptyModel); + if (isLast && !isAddingRow) { + isAddingRow = true; + formRows.add(kFormDataEmptyModel); + } _onFieldChange(selectedId!); }, colorScheme: Theme.of(context).colorScheme, diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart index e5b0df59..f473ec3f 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart @@ -19,6 +19,7 @@ class EditRequestHeadersState extends ConsumerState { final random = Random.secure(); late List headerRows; late List isRowEnabledList; + bool isAddingRow = false; @override void initState() { @@ -52,6 +53,7 @@ class EditRequestHeadersState extends ConsumerState { ref.read(selectedRequestModelProvider)?.isHeaderEnabledList ?? List.filled(rH?.length ?? 0, true, growable: true); isRowEnabledList.add(false); + isAddingRow = false; DaviModel model = DaviModel( rows: headerRows, @@ -90,7 +92,8 @@ class EditRequestHeadersState extends ConsumerState { hintText: "Add Header Name", onChanged: (value) { headerRows[idx] = headerRows[idx].copyWith(name: value); - if (isLast) { + if (isLast && !isAddingRow) { + isAddingRow = true; isRowEnabledList[idx] = true; headerRows.add(kNameValueEmptyModel); isRowEnabledList.add(false); @@ -123,7 +126,8 @@ class EditRequestHeadersState extends ConsumerState { hintText: " Add Header Value", onChanged: (value) { headerRows[idx] = headerRows[idx].copyWith(value: value); - if (isLast) { + if (isLast && !isAddingRow) { + isAddingRow = true; isRowEnabledList[idx] = true; headerRows.add(kNameValueEmptyModel); isRowEnabledList.add(false); diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart index 319330ba..7002f8eb 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart @@ -20,6 +20,7 @@ class EditRequestURLParamsState extends ConsumerState { final random = Random.secure(); late List paramRows; late List isRowEnabledList; + bool isAddingRow = false; @override void initState() { @@ -52,6 +53,7 @@ class EditRequestURLParamsState extends ConsumerState { ref.read(selectedRequestModelProvider)?.isParamEnabledList ?? List.filled(rP?.length ?? 0, true, growable: true); isRowEnabledList.add(false); + isAddingRow = false; DaviModel model = DaviModel( rows: paramRows, @@ -90,7 +92,8 @@ class EditRequestURLParamsState extends ConsumerState { hintText: "Add URL Parameter", onChanged: (value) { paramRows[idx] = paramRows[idx].copyWith(name: value); - if (isLast) { + if (isLast && !isAddingRow) { + isAddingRow = true; isRowEnabledList[idx] = true; paramRows.add(kNameValueEmptyModel); isRowEnabledList.add(false); @@ -123,7 +126,8 @@ class EditRequestURLParamsState extends ConsumerState { hintText: "Add Value", onChanged: (value) { paramRows[idx] = paramRows[idx].copyWith(value: value); - if (isLast) { + if (isLast && !isAddingRow) { + isAddingRow = true; isRowEnabledList[idx] = true; paramRows.add(kNameValueEmptyModel); isRowEnabledList.add(false); From 459ab83eb2c993a0ceab399e5925d61eb5c21ae0 Mon Sep 17 00:00:00 2001 From: Sudhar08 <99385366+sudhar08@users.noreply.github.com> Date: Fri, 22 Mar 2024 21:27:39 +0530 Subject: [PATCH 14/25] feat: Add a search/filter for collection pane #305 This feature adds a search/filter functionality to the collection pane, allowing users to easily search for specific requests based on their names. Users can input search queries to filter the requests displayed in the collection pane, improving the navigation and organization of requests within the application. --- lib/providers/collection_providers.dart | 35 +++++++++++++++++++++- lib/screens/home_page/collection_pane.dart | 34 ++++++++++++++++++--- 2 files changed, 64 insertions(+), 5 deletions(-) diff --git a/lib/providers/collection_providers.dart b/lib/providers/collection_providers.dart index db6b7cf3..30801ecb 100644 --- a/lib/providers/collection_providers.dart +++ b/lib/providers/collection_providers.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'settings_providers.dart'; import 'ui_providers.dart'; @@ -8,6 +9,7 @@ import '../consts.dart'; import 'package:http/http.dart' as http; final selectedIdStateProvider = StateProvider((ref) => null); +final searchQueryProvider = StateProvider((ref) => ''); final selectedRequestModelProvider = StateProvider((ref) { final selectedId = ref.watch(selectedIdStateProvider); @@ -31,7 +33,7 @@ final StateNotifierProvider?> class CollectionStateNotifier extends StateNotifier?> { CollectionStateNotifier(this.ref, this.hiveHandler) : super(null) { - var status = loadData(); + var status = filterRequests(ref.read(searchQueryProvider.notifier).state); Future.microtask(() { if (status) { ref.read(requestSequenceProvider.notifier).state = [ @@ -248,6 +250,37 @@ class CollectionStateNotifier } } + bool filterRequests(String query) { + if (query.isEmpty) { + + loadData(); + return true; + } else { + // Filter requests based on the query + final filteredRequests = state?.values.where((request) => + request.name.toLowerCase().contains(query.toLowerCase())).toList(); + + + if (filteredRequests != null && filteredRequests.isNotEmpty) { + + Map requestMap = {}; + + for (var request in filteredRequests) { + requestMap[request.id] = request; + } + + state = requestMap; + + } else { + print("No matching requests found"); + } + + + + return false; + } + } + Future saveData() async { ref.read(saveDataStateProvider.notifier).state = true; final saveResponse = ref.read(settingsProvider).saveResponses; diff --git a/lib/screens/home_page/collection_pane.dart b/lib/screens/home_page/collection_pane.dart index fd0b518a..c44ed56e 100644 --- a/lib/screens/home_page/collection_pane.dart +++ b/lib/screens/home_page/collection_pane.dart @@ -1,3 +1,4 @@ +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:apidash/providers/providers.dart'; @@ -66,10 +67,33 @@ class CollectionPane extends ConsumerWidget { style: kTextStyleButton, ), ), + ], ), ), - kVSpacer8, + kVSpacer10, + SizedBox( + height:30, + child:SearchBar( + + onChanged: (value){ + if(value.isNotEmpty){ + ref.read(searchQueryProvider.notifier).state = value; + ref.read(collectionStateNotifierProvider.notifier) + .filterRequests(value); + } + else{ + + } + + }, + hintText: "search", + leading: Icon(Icons.search), + textStyle: MaterialStateTextStyle.resolveWith((states) => TextStyle(fontSize: 15.0)), + elevation: MaterialStateProperty.all(2.0), + )), + kVSpacer10, + const Expanded( child: RequestList(), ), @@ -110,7 +134,7 @@ class _RequestListState extends ConsumerState { final alwaysShowCollectionPaneScrollbar = ref.watch(settingsProvider .select((value) => value.alwaysShowCollectionPaneScrollbar)); - return Scrollbar( + return requestItems.isNotEmpty?Scrollbar( controller: controller, thumbVisibility: alwaysShowCollectionPaneScrollbar ? true : null, radius: const Radius.circular(12), @@ -118,7 +142,7 @@ class _RequestListState extends ConsumerState { padding: kPr8CollectionPane, scrollController: controller, buildDefaultDragHandles: false, - itemCount: requestSequence.length, + itemCount: requestItems.length, onReorder: (int oldIndex, int newIndex) { if (oldIndex < newIndex) { newIndex -= 1; @@ -131,6 +155,8 @@ class _RequestListState extends ConsumerState { }, itemBuilder: (context, index) { var id = requestSequence[index]; + print(requestItems[id]!); + return ReorderableDragStartListener( key: ValueKey(id), index: index, @@ -144,7 +170,7 @@ class _RequestListState extends ConsumerState { ); }, ), - ); + ):Text("data"); } } From b73a4ab9c4d186a362815adc335075d0b72a2fc4 Mon Sep 17 00:00:00 2001 From: alainjr10 Date: Fri, 22 Mar 2024 22:07:05 +0100 Subject: [PATCH 15/25] fix: fixed codegen dropdown overflow --- lib/widgets/codegen_previewer.dart | 2 +- lib/widgets/dropdowns.dart | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/widgets/codegen_previewer.dart b/lib/widgets/codegen_previewer.dart index 298610c7..b09cc577 100644 --- a/lib/widgets/codegen_previewer.dart +++ b/lib/widgets/codegen_previewer.dart @@ -135,7 +135,7 @@ class ViewCodePane extends StatelessWidget { child: Column( children: [ SizedBox( - height: kHeaderHeight, + height: constraints.maxWidth > 340 ? kHeaderHeight : kTabHeight, child: Row( children: [ Expanded( diff --git a/lib/widgets/dropdowns.dart b/lib/widgets/dropdowns.dart index 69008a6b..b40122fc 100644 --- a/lib/widgets/dropdowns.dart +++ b/lib/widgets/dropdowns.dart @@ -157,6 +157,7 @@ class DropdownButtonCodegenLanguage extends StatelessWidget { Widget build(BuildContext context) { final surfaceColor = Theme.of(context).colorScheme.surface; return DropdownButton( + isExpanded: true, focusColor: surfaceColor, value: codegenLanguage, icon: const Icon( From e3084e884494b9994bf7a5a5c17f0c8a447eacbe Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 23 Mar 2024 14:19:24 +0530 Subject: [PATCH 16/25] fixes --- lib/widgets/codegen_previewer.dart | 2 +- lib/widgets/dropdowns.dart | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/widgets/codegen_previewer.dart b/lib/widgets/codegen_previewer.dart index b09cc577..298610c7 100644 --- a/lib/widgets/codegen_previewer.dart +++ b/lib/widgets/codegen_previewer.dart @@ -135,7 +135,7 @@ class ViewCodePane extends StatelessWidget { child: Column( children: [ SizedBox( - height: constraints.maxWidth > 340 ? kHeaderHeight : kTabHeight, + height: kHeaderHeight, child: Row( children: [ Expanded( diff --git a/lib/widgets/dropdowns.dart b/lib/widgets/dropdowns.dart index b40122fc..1be902e9 100644 --- a/lib/widgets/dropdowns.dart +++ b/lib/widgets/dropdowns.dart @@ -182,6 +182,8 @@ class DropdownButtonCodegenLanguage extends StatelessWidget { child: Text( value.label, style: kTextStyleButton, + overflow: TextOverflow.ellipsis, + maxLines: 1, ), ), ); From c38c1846f50c7363b88e818cea3d5ce999442062 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 23 Mar 2024 14:56:22 +0530 Subject: [PATCH 17/25] Update padding in request tables --- lib/consts.dart | 4 ++++ .../details_card/request_pane/request_form_data.dart | 3 ++- .../details_card/request_pane/request_headers.dart | 3 ++- .../editor_pane/details_card/request_pane/request_params.dart | 3 ++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/consts.dart b/lib/consts.dart index 01bc5593..277f6956 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -102,6 +102,9 @@ const kP8CollectionPane = EdgeInsets.only( const kPb10 = EdgeInsets.only( bottom: 10, ); +const kPb15 = EdgeInsets.only( + bottom: 15, +); const kPr8CollectionPane = EdgeInsets.only(right: 8.0); const kpsV5 = EdgeInsets.symmetric(vertical: 2); const kHSpacer4 = SizedBox(width: 4); @@ -112,6 +115,7 @@ const kVSpacer5 = SizedBox(height: 5); const kVSpacer8 = SizedBox(height: 8); const kVSpacer10 = SizedBox(height: 10); const kVSpacer20 = SizedBox(height: 20); +const kVSpacer40 = SizedBox(height: 40); const kTabAnimationDuration = Duration(milliseconds: 200); const kTabHeight = 45.0; diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart index b3d8f67c..ef05f4d6 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart @@ -208,13 +208,14 @@ class _FormDataBodyState extends ConsumerState { child: Davi(daviModelRows), ), ), + kVSpacer20, ], ), ), Align( alignment: Alignment.bottomCenter, child: Padding( - padding: const EdgeInsets.only(bottom: 30), + padding: const EdgeInsets.only(bottom: 5), child: ElevatedButton.icon( onPressed: () { formRows.add(kFormDataEmptyModel); diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart index e5b0df59..28fd0d3d 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart @@ -182,13 +182,14 @@ class EditRequestHeadersState extends ConsumerState { child: Davi(model), ), ), + kVSpacer40, ], ), ), Align( alignment: Alignment.bottomCenter, child: Padding( - padding: const EdgeInsets.only(bottom: 30), + padding: kPb15, child: ElevatedButton.icon( onPressed: () { headerRows.add(kNameValueEmptyModel); diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart index 319330ba..9bbd9d1d 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart @@ -182,13 +182,14 @@ class EditRequestURLParamsState extends ConsumerState { child: Davi(model), ), ), + kVSpacer40, ], ), ), Align( alignment: Alignment.bottomCenter, child: Padding( - padding: const EdgeInsets.only(bottom: 30), + padding: kPb15, child: ElevatedButton.icon( onPressed: () { paramRows.add(kNameValueEmptyModel); From 19c8406d364b0675e3d46796017b4c6b814a2216 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 23 Mar 2024 17:50:19 +0530 Subject: [PATCH 18/25] Fix for clearResponse --- lib/models/request_model.dart | 2 ++ lib/providers/collection_providers.dart | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/models/request_model.dart b/lib/models/request_model.dart index 0ea6d735..acf6a3eb 100644 --- a/lib/models/request_model.dart +++ b/lib/models/request_model.dart @@ -95,6 +95,7 @@ class RequestModel { RequestModel duplicate({ required String id, String? name, + int? requestTabIndex, }) { return RequestModel( id: id, @@ -102,6 +103,7 @@ class RequestModel { url: url, name: name ?? "${this.name} (copy)", description: description, + requestTabIndex: requestTabIndex ?? 0, requestHeaders: requestHeaders != null ? [...requestHeaders!] : null, requestParams: requestParams != null ? [...requestParams!] : null, isHeaderEnabledList: diff --git a/lib/providers/collection_providers.dart b/lib/providers/collection_providers.dart index db6b7cf3..75e9d7c5 100644 --- a/lib/providers/collection_providers.dart +++ b/lib/providers/collection_providers.dart @@ -102,6 +102,7 @@ class CollectionStateNotifier final newModel = currentModel.duplicate( id: id, name: currentModel.name, + requestTabIndex: currentModel.requestTabIndex, ); var map = {...state!}; map[id] = newModel; From 0884a1405835ba3dc98000dfa7bec60b95a9e8b3 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 23 Mar 2024 17:51:49 +0530 Subject: [PATCH 19/25] Code cleanup & Refactoring --- lib/consts.dart | 42 +++++++++- lib/screens/home_page/collection_pane.dart | 4 +- .../request_pane/request_form_data.dart | 18 ++-- .../request_pane/request_headers.dart | 12 +-- .../request_pane/request_params.dart | 12 +-- .../details_card/response_pane.dart | 3 - lib/widgets/buttons.dart | 15 ++-- lib/widgets/request_widgets.dart | 18 ++-- lib/widgets/response_widgets.dart | 83 +++++-------------- lib/widgets/tabs.dart | 10 ++- 10 files changed, 103 insertions(+), 114 deletions(-) diff --git a/lib/consts.dart b/lib/consts.dart index 277f6956..2ad9d185 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -56,6 +56,7 @@ const kForegroundOpacity = 0.05; const kOverlayBackgroundOpacity = 0.5; const kTextStyleButton = TextStyle(fontWeight: FontWeight.bold); +const kTextStyleTab = TextStyle(fontSize: 14); const kTextStyleButtonSmall = TextStyle(fontSize: 12); const kFormDataButtonLabelTextStyle = TextStyle( fontSize: 12, @@ -71,9 +72,13 @@ const kP5 = EdgeInsets.all(5); const kP8 = EdgeInsets.all(8); const kPs8 = EdgeInsets.only(left: 8); const kPs2 = EdgeInsets.only(left: 2); +const kPe8 = EdgeInsets.only(right: 8.0); const kPh20v5 = EdgeInsets.symmetric(horizontal: 20, vertical: 5); const kPh20v10 = EdgeInsets.symmetric(horizontal: 20, vertical: 10); const kP10 = EdgeInsets.all(10); +const kPv8 = EdgeInsets.symmetric(vertical: 8); +const kPv2 = EdgeInsets.symmetric(vertical: 2); +const kPh2 = EdgeInsets.symmetric(horizontal: 2); const kPt24o8 = EdgeInsets.only(top: 24, left: 8.0, right: 8.0, bottom: 8.0); const kPt5o10 = EdgeInsets.only(left: 10.0, right: 10.0, top: 5.0, bottom: 10.0); @@ -105,8 +110,6 @@ const kPb10 = EdgeInsets.only( const kPb15 = EdgeInsets.only( bottom: 15, ); -const kPr8CollectionPane = EdgeInsets.only(right: 8.0); -const kpsV5 = EdgeInsets.symmetric(vertical: 2); const kHSpacer4 = SizedBox(width: 4); const kHSpacer5 = SizedBox(width: 5); const kHSpacer10 = SizedBox(width: 10); @@ -118,7 +121,7 @@ const kVSpacer20 = SizedBox(height: 20); const kVSpacer40 = SizedBox(height: 40); const kTabAnimationDuration = Duration(milliseconds: 200); -const kTabHeight = 45.0; +const kTabHeight = 32.0; const kHeaderHeight = 32.0; const kSegmentHeight = 24.0; const kTextButtonMinWidth = 44.0; @@ -537,3 +540,36 @@ const kLabelSave = "Save"; const kLabelDownload = "Download"; const kLabelSaving = "Saving"; const kLabelSaved = "Saved"; +// Request Pane +const kLabelRequest = "Request"; +const kLabelHideCode = "Hide Code"; +const kLabelViewCode = "View Code"; +const kLabelURLParams = "URL Params"; +const kLabelHeaders = "Headers"; +const kLabelBody = "Body"; +const kNameCheckbox = "Checkbox"; +const kNameURLParam = "URL Parameter"; +const kNameHeader = "Header Name"; +const kNameValue = "Value"; +const kNameField = "Field"; +const kHintAddURLParam = "Add URL Parameter"; +const kHintAddValue = "Add Value"; +const kHintAddName = "Add Name"; +const kHintAddFieldName = "Add Field Name"; +const kLabelAddParam = "Add Param"; +const kLabelAddHeader = "Add Header"; +const kLabelSelectFile = "Select File"; +const kLabelAddFormField = "Add Form Field"; +// Response Pane +const kLabelNotSent = "Not Sent"; +const kLabelResponse = "Response"; +const kLabelResponseBody = "Response Body"; +const kTooltipClearResponse = "Clear Response"; +const kHeaderRow = ["Header Name", "Header Value"]; +const kLabelRequestHeaders = "Request Headers"; +const kLabelResponseHeaders = "Response Headers"; +const kLabelItems = "items"; +const kMsgError = "Error: Response data does not exist."; +const kMsgNullBody = "Response body is missing (null)."; +const kMsgNoContent = "No content"; +const kMsgUnknowContentType = "Unknown Response Content-Type"; diff --git a/lib/screens/home_page/collection_pane.dart b/lib/screens/home_page/collection_pane.dart index fd0b518a..bdcd9b5f 100644 --- a/lib/screens/home_page/collection_pane.dart +++ b/lib/screens/home_page/collection_pane.dart @@ -26,7 +26,7 @@ class CollectionPane extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Padding( - padding: kPr8CollectionPane, + padding: kPe8, child: Wrap( alignment: WrapAlignment.spaceBetween, children: [ @@ -115,7 +115,7 @@ class _RequestListState extends ConsumerState { thumbVisibility: alwaysShowCollectionPaneScrollbar ? true : null, radius: const Radius.circular(12), child: ReorderableListView.builder( - padding: kPr8CollectionPane, + padding: kPe8, scrollController: controller, buildDefaultDragHandles: false, itemCount: requestSequence.length, diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart index 61bf9d59..a6bafd6e 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_form_data.dart @@ -52,8 +52,8 @@ class _FormDataBodyState extends ConsumerState { rows: formRows, columns: [ DaviColumn( - cellPadding: kpsV5, - name: 'Key', + cellPadding: kPv2, + name: kNameField, grow: 4, cellBuilder: (_, row) { int idx = row.index; @@ -63,7 +63,7 @@ class _FormDataBodyState extends ConsumerState { child: FormDataField( keyId: "$selectedId-$idx-form-v-$seed", initialValue: formRows[idx].name, - hintText: " Add Key", + hintText: kHintAddFieldName, onChanged: (value) { formRows[idx] = formRows[idx].copyWith(name: value); if (isLast && !isAddingRow) { @@ -93,7 +93,7 @@ class _FormDataBodyState extends ConsumerState { ), DaviColumn( width: 40, - cellPadding: kpsV5, + cellPadding: kPv2, cellAlignment: Alignment.center, cellBuilder: (_, row) { return Text( @@ -103,9 +103,9 @@ class _FormDataBodyState extends ConsumerState { }, ), DaviColumn( - name: 'Value', + name: kNameValue, grow: 4, - cellPadding: kpsV5, + cellPadding: kPv2, cellBuilder: (_, row) { int idx = row.index; bool isLast = idx + 1 == formRows.length; @@ -145,7 +145,7 @@ class _FormDataBodyState extends ConsumerState { (formRows[idx].type == FormDataType.file && formRows[idx].value.isNotEmpty) ? formRows[idx].value.toString() - : "Select File", + : kLabelSelectFile, textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, style: kFormDataButtonLabelTextStyle, @@ -159,7 +159,7 @@ class _FormDataBodyState extends ConsumerState { : CellField( keyId: "$selectedId-$idx-form-v-$seed", initialValue: formRows[idx].value, - hintText: " Add Value", + hintText: kHintAddValue, onChanged: (value) { formRows[idx] = formRows[idx].copyWith(value: value); if (isLast && !isAddingRow) { @@ -231,7 +231,7 @@ class _FormDataBodyState extends ConsumerState { }, icon: const Icon(Icons.add), label: const Text( - "Add Form Data", + kLabelAddFormField, style: kTextStyleButton, ), ), diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart index 0e09702c..ee5c574c 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_headers.dart @@ -59,7 +59,7 @@ class EditRequestHeadersState extends ConsumerState { rows: headerRows, columns: [ DaviColumn( - name: 'Checkbox', + name: kNameCheckbox, width: 30, cellBuilder: (_, row) { int idx = row.index; @@ -80,7 +80,7 @@ class EditRequestHeadersState extends ConsumerState { }, ), DaviColumn( - name: 'Header Name', + name: kNameHeader, width: 70, grow: 1, cellBuilder: (_, row) { @@ -89,7 +89,7 @@ class EditRequestHeadersState extends ConsumerState { return HeaderField( keyId: "$selectedId-$idx-headers-k-$seed", initialValue: headerRows[idx].name, - hintText: "Add Header Name", + hintText: kHintAddName, onChanged: (value) { headerRows[idx] = headerRows[idx].copyWith(name: value); if (isLast && !isAddingRow) { @@ -115,7 +115,7 @@ class EditRequestHeadersState extends ConsumerState { }, ), DaviColumn( - name: 'Header Value', + name: kNameValue, grow: 1, cellBuilder: (_, row) { int idx = row.index; @@ -123,7 +123,7 @@ class EditRequestHeadersState extends ConsumerState { return CellField( keyId: "$selectedId-$idx-headers-v-$seed", initialValue: headerRows[idx].value, - hintText: " Add Header Value", + hintText: kHintAddValue, onChanged: (value) { headerRows[idx] = headerRows[idx].copyWith(value: value); if (isLast && !isAddingRow) { @@ -202,7 +202,7 @@ class EditRequestHeadersState extends ConsumerState { }, icon: const Icon(Icons.add), label: const Text( - "Add Header", + kLabelAddHeader, style: kTextStyleButton, ), ), diff --git a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart index 2e690f56..47dca1a6 100644 --- a/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart +++ b/lib/screens/home_page/editor_pane/details_card/request_pane/request_params.dart @@ -59,7 +59,7 @@ class EditRequestURLParamsState extends ConsumerState { rows: paramRows, columns: [ DaviColumn( - name: 'Checkbox', + name: kNameCheckbox, width: 30, cellBuilder: (_, row) { int idx = row.index; @@ -80,7 +80,7 @@ class EditRequestURLParamsState extends ConsumerState { }, ), DaviColumn( - name: 'URL Parameter', + name: kNameURLParam, width: 70, grow: 1, cellBuilder: (_, row) { @@ -89,7 +89,7 @@ class EditRequestURLParamsState extends ConsumerState { return CellField( keyId: "$selectedId-$idx-params-k-$seed", initialValue: paramRows[idx].name, - hintText: "Add URL Parameter", + hintText: kHintAddURLParam, onChanged: (value) { paramRows[idx] = paramRows[idx].copyWith(name: value); if (isLast && !isAddingRow) { @@ -115,7 +115,7 @@ class EditRequestURLParamsState extends ConsumerState { }, ), DaviColumn( - name: 'Value', + name: kNameValue, grow: 1, cellBuilder: (_, row) { int idx = row.index; @@ -123,7 +123,7 @@ class EditRequestURLParamsState extends ConsumerState { return CellField( keyId: "$selectedId-$idx-params-v-$seed", initialValue: paramRows[idx].value, - hintText: "Add Value", + hintText: kHintAddValue, onChanged: (value) { paramRows[idx] = paramRows[idx].copyWith(value: value); if (isLast && !isAddingRow) { @@ -202,7 +202,7 @@ class EditRequestURLParamsState extends ConsumerState { }, icon: const Icon(Icons.add), label: const Text( - "Add Param", + kLabelAddParam, style: kTextStyleButton, ), ), diff --git a/lib/screens/home_page/editor_pane/details_card/response_pane.dart b/lib/screens/home_page/editor_pane/details_card/response_pane.dart index 703b873d..c1c770cf 100644 --- a/lib/screens/home_page/editor_pane/details_card/response_pane.dart +++ b/lib/screens/home_page/editor_pane/details_card/response_pane.dart @@ -34,7 +34,6 @@ class ResponseDetails extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - var sm = ScaffoldMessenger.of(context); final responseStatus = ref.watch( selectedRequestModelProvider.select((value) => value?.responseStatus)); final message = ref @@ -52,8 +51,6 @@ class ResponseDetails extends ConsumerWidget { ref .read(collectionStateNotifierProvider.notifier) .clearResponse(selectedRequest?.id); - sm.hideCurrentSnackBar(); - sm.showSnackBar(getSnackBar('Response cleared')); }, ), const Expanded( diff --git a/lib/widgets/buttons.dart b/lib/widgets/buttons.dart index f4280380..41bcddb9 100644 --- a/lib/widgets/buttons.dart +++ b/lib/widgets/buttons.dart @@ -246,15 +246,12 @@ class ClearResponseButton extends StatelessWidget { @override Widget build(BuildContext context) { - return Tooltip( - message: 'Clear response', - child: TextButton( - style: TextButton.styleFrom(minimumSize: const Size(40, 40)), - onPressed: onPressed, - child: const Icon( - Icons.delete, - size: 20, - ), + return IconButton( + tooltip: kTooltipClearResponse, + onPressed: onPressed, + icon: const Icon( + Icons.delete, + size: 16, ), ); } diff --git a/lib/widgets/request_widgets.dart b/lib/widgets/request_widgets.dart index ffac504e..7375a3b4 100644 --- a/lib/widgets/request_widgets.dart +++ b/lib/widgets/request_widgets.dart @@ -48,16 +48,12 @@ class _RequestPaneState extends State return Column( children: [ Padding( - padding: kPh20v10, + padding: kP8, child: SizedBox( height: kHeaderHeight, child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.end, children: [ - Text( - "Request", - style: Theme.of(context).textTheme.titleMedium, - ), FilledButton.tonalIcon( onPressed: widget.onPressedCodeButton, icon: Icon( @@ -68,7 +64,8 @@ class _RequestPaneState extends State label: SizedBox( width: 75, child: Text( - widget.codePaneVisible ? "Hide Code" : "View Code"), + widget.codePaneVisible ? kLabelHideCode : kLabelViewCode, + ), ), ), ], @@ -79,18 +76,19 @@ class _RequestPaneState extends State key: Key(widget.selectedId!), controller: _controller, overlayColor: kColorTransparentState, + labelPadding: kPh2, onTap: widget.onTapTabBar, tabs: [ TabLabel( - text: 'URL Params', + text: kLabelURLParams, showIndicator: widget.showIndicators[0], ), TabLabel( - text: 'Headers', + text: kLabelHeaders, showIndicator: widget.showIndicators[1], ), TabLabel( - text: 'Body', + text: kLabelBody, showIndicator: widget.showIndicators[2], ), ], diff --git a/lib/widgets/response_widgets.dart b/lib/widgets/response_widgets.dart index e641b03d..0b570c19 100644 --- a/lib/widgets/response_widgets.dart +++ b/lib/widgets/response_widgets.dart @@ -23,7 +23,7 @@ class NotSentWidget extends StatelessWidget { color: color, ), Text( - 'Not Sent', + kLabelNotSent, style: Theme.of(context).textTheme.titleMedium?.copyWith(color: color), ), @@ -66,42 +66,19 @@ class ResponsePaneHeader extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: kPh20v10, + padding: kPv8, child: SizedBox( height: kHeaderHeight, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text.rich( - TextSpan( - children: [ - const TextSpan( - text: "Response (", - ), - TextSpan( - text: "$responseStatus", - style: TextStyle( - color: getResponseStatusCodeColor( - responseStatus, - brightness: Theme.of(context).brightness, - ), - fontFamily: kCodeStyle.fontFamily, - ), - ), - const TextSpan( - text: ")", - ), - ], - style: Theme.of(context).textTheme.titleMedium, - ), - ), - kHSpacer20, + kHSpacer10, Expanded( child: Text( - message ?? "", + "$responseStatus: ${message ?? '-'}", softWrap: false, overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.titleMedium!.copyWith( + style: Theme.of(context).textTheme.bodyMedium?.copyWith( fontFamily: kCodeStyle.fontFamily, color: getResponseStatusCodeColor( responseStatus, @@ -110,10 +87,10 @@ class ResponsePaneHeader extends StatelessWidget { ), ), ), - kHSpacer20, + kHSpacer10, Text( humanizeDuration(time), - style: Theme.of(context).textTheme.titleMedium!.copyWith( + style: Theme.of(context).textTheme.bodyMedium?.copyWith( fontFamily: kCodeStyle.fontFamily, color: Theme.of(context).colorScheme.secondary, ), @@ -163,31 +140,15 @@ class _ResponseTabViewState extends State TabBar( key: Key(widget.selectedId!), controller: _controller, + labelPadding: kPh2, overlayColor: kColorTransparentState, onTap: (index) {}, tabs: const [ - SizedBox( - height: kTabHeight, - child: Center( - child: Text( - 'Body', - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - softWrap: false, - style: kTextStyleButton, - ), - ), + TabLabel( + text: kLabelResponseBody, ), - SizedBox( - height: kTabHeight, - child: Center( - child: Text( - 'Headers', - textAlign: TextAlign.center, - overflow: TextOverflow.fade, - style: kTextStyleButton, - ), - ), + TabLabel( + text: kLabelHeaders, ), ], ), @@ -227,8 +188,8 @@ class ResponseHeadersHeader extends StatelessWidget { children: [ Expanded( child: Text( - "$name (${map.length} items)", - style: Theme.of(context).textTheme.labelLarge!.copyWith( + "$name (${map.length} $kLabelItems)", + style: Theme.of(context).textTheme.labelMedium?.copyWith( fontWeight: FontWeight.bold, ), ), @@ -243,8 +204,6 @@ class ResponseHeadersHeader extends StatelessWidget { } } -const kHeaderRow = ["Header Name", "Header Value"]; - class ResponseHeaders extends StatelessWidget { const ResponseHeaders({ super.key, @@ -263,7 +222,7 @@ class ResponseHeaders extends StatelessWidget { children: [ ResponseHeadersHeader( map: responseHeaders, - name: "Response Headers", + name: kLabelResponseHeaders, ), if (responseHeaders.isNotEmpty) kVSpacer5, if (responseHeaders.isNotEmpty) @@ -275,7 +234,7 @@ class ResponseHeaders extends StatelessWidget { kVSpacer10, ResponseHeadersHeader( map: requestHeaders, - name: "Request Headers", + name: kLabelRequestHeaders, ), if (requestHeaders.isNotEmpty) kVSpacer5, if (requestHeaders.isNotEmpty) @@ -302,20 +261,18 @@ class ResponseBody extends StatelessWidget { Widget build(BuildContext context) { final responseModel = selectedRequestModel?.responseModel; if (responseModel == null) { - return const ErrorMessage( - message: - 'Error: Response data does not exist. $kUnexpectedRaiseIssue'); + return const ErrorMessage(message: '$kMsgError $kUnexpectedRaiseIssue'); } var body = responseModel.body; var formattedBody = responseModel.formattedBody; if (body == null) { return const ErrorMessage( - message: 'Response body is missing (null). $kUnexpectedRaiseIssue'); + message: '$kMsgNullBody $kUnexpectedRaiseIssue'); } if (body.isEmpty) { return const ErrorMessage( - message: 'No content', + message: kMsgNoContent, showIcon: false, showIssueButton: false, ); @@ -325,7 +282,7 @@ class ResponseBody extends StatelessWidget { if (mediaType == null) { return ErrorMessage( message: - 'Unknown Response Content-Type - ${responseModel.contentType}. $kUnexpectedRaiseIssue'); + '$kMsgUnknowContentType - ${responseModel.contentType}. $kUnexpectedRaiseIssue'); } var responseBodyView = getResponseBodyViewOptions(mediaType); diff --git a/lib/widgets/tabs.dart b/lib/widgets/tabs.dart index eabf36c8..c2542a26 100644 --- a/lib/widgets/tabs.dart +++ b/lib/widgets/tabs.dart @@ -2,7 +2,11 @@ import 'package:flutter/material.dart'; import 'package:apidash/consts.dart'; class TabLabel extends StatelessWidget { - const TabLabel({super.key, required this.text, this.showIndicator = false}); + const TabLabel({ + super.key, + required this.text, + this.showIndicator = false, + }); final String text; final bool showIndicator; @@ -18,14 +22,14 @@ class TabLabel extends StatelessWidget { textAlign: TextAlign.center, overflow: TextOverflow.fade, softWrap: false, - style: kTextStyleButton, + style: kTextStyleTab, ), ), if (showIndicator) const Align( alignment: Alignment.topCenter, child: Padding( - padding: EdgeInsets.only(top: 6), + padding: EdgeInsets.only(top: 1), child: Icon( Icons.circle, size: 6, From 345f71a7c05b6f1a221f7ff4902e13f79d16035f Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 23 Mar 2024 18:05:20 +0530 Subject: [PATCH 20/25] Fix tests --- test/widgets/request_widgets_test.dart | 4 ---- test/widgets/response_widgets_test.dart | 9 +++------ 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/test/widgets/request_widgets_test.dart b/test/widgets/request_widgets_test.dart index b50a033f..b47114b8 100644 --- a/test/widgets/request_widgets_test.dart +++ b/test/widgets/request_widgets_test.dart @@ -21,7 +21,6 @@ void main() { ); expect(find.byType(Center), findsAtLeastNWidgets(1)); - expect(find.text('Request'), findsOneWidget); expect(find.text('Hide Code'), findsOneWidget); expect(find.text('View Code'), findsNothing); expect(find.text('URL Params'), findsOneWidget); @@ -52,7 +51,6 @@ void main() { ); expect(find.byType(Center), findsAtLeastNWidgets(1)); - expect(find.text('Request'), findsOneWidget); expect(find.text('Hide Code'), findsOneWidget); expect(find.text('View Code'), findsNothing); expect(find.text('URL Params'), findsOneWidget); @@ -83,7 +81,6 @@ void main() { ); expect(find.byType(Center), findsAtLeastNWidgets(1)); - expect(find.text('Request'), findsOneWidget); expect(find.text('Hide Code'), findsNothing); expect(find.text('View Code'), findsOneWidget); expect(find.text('URL Params'), findsOneWidget); @@ -117,7 +114,6 @@ void main() { ); expect(find.byType(Center), findsAtLeastNWidgets(1)); - expect(find.text('Request'), findsOneWidget); expect(find.text('URL Params'), findsOneWidget); expect(find.text('Headers'), findsOneWidget); expect(find.text('Body'), findsOneWidget); diff --git a/test/widgets/response_widgets_test.dart b/test/widgets/response_widgets_test.dart index 7877a285..f6f3ecdd 100644 --- a/test/widgets/response_widgets_test.dart +++ b/test/widgets/response_widgets_test.dart @@ -52,11 +52,8 @@ void main() { ), ); - expect(find.byType(RichText), findsAtLeastNWidgets(1)); - expect( - find.textContaining("Response (", findRichText: true), findsOneWidget); - expect(find.text('Hi'), findsOneWidget); expect(find.textContaining("200", findRichText: true), findsOneWidget); + expect(find.textContaining("Hi", findRichText: true), findsOneWidget); expect(find.text(humanizeDuration(const Duration(microseconds: 23))), findsOneWidget); }); @@ -73,7 +70,7 @@ void main() { ), ); - expect(find.text('Body'), findsOneWidget); + expect(find.text('Response Body'), findsOneWidget); expect(find.text('Headers'), findsOneWidget); await tester.tap(find.text('Headers')); @@ -81,7 +78,7 @@ void main() { expect(find.text('first'), findsNothing); expect(find.text('second'), findsOneWidget); - await tester.tap(find.text('Body')); + await tester.tap(find.text('Response Body')); await tester.pumpAndSettle(); expect(find.text('first'), findsOneWidget); From 5a9dd7c07e98304fc4b6594d6376aa61fdf232d1 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 24 Mar 2024 01:18:28 +0530 Subject: [PATCH 21/25] Refactor --- lib/providers/collection_providers.dart | 35 +------------ lib/providers/ui_providers.dart | 2 + lib/screens/home_page/collection_pane.dart | 61 +++++++++++++--------- lib/widgets/textfields.dart | 31 +++++++++-- 4 files changed, 66 insertions(+), 63 deletions(-) diff --git a/lib/providers/collection_providers.dart b/lib/providers/collection_providers.dart index 30801ecb..db6b7cf3 100644 --- a/lib/providers/collection_providers.dart +++ b/lib/providers/collection_providers.dart @@ -1,4 +1,3 @@ -import 'package:collection/collection.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'settings_providers.dart'; import 'ui_providers.dart'; @@ -9,7 +8,6 @@ import '../consts.dart'; import 'package:http/http.dart' as http; final selectedIdStateProvider = StateProvider((ref) => null); -final searchQueryProvider = StateProvider((ref) => ''); final selectedRequestModelProvider = StateProvider((ref) { final selectedId = ref.watch(selectedIdStateProvider); @@ -33,7 +31,7 @@ final StateNotifierProvider?> class CollectionStateNotifier extends StateNotifier?> { CollectionStateNotifier(this.ref, this.hiveHandler) : super(null) { - var status = filterRequests(ref.read(searchQueryProvider.notifier).state); + var status = loadData(); Future.microtask(() { if (status) { ref.read(requestSequenceProvider.notifier).state = [ @@ -250,37 +248,6 @@ class CollectionStateNotifier } } - bool filterRequests(String query) { - if (query.isEmpty) { - - loadData(); - return true; - } else { - // Filter requests based on the query - final filteredRequests = state?.values.where((request) => - request.name.toLowerCase().contains(query.toLowerCase())).toList(); - - - if (filteredRequests != null && filteredRequests.isNotEmpty) { - - Map requestMap = {}; - - for (var request in filteredRequests) { - requestMap[request.id] = request; - } - - state = requestMap; - - } else { - print("No matching requests found"); - } - - - - return false; - } - } - Future saveData() async { ref.read(saveDataStateProvider.notifier).state = true; final saveResponse = ref.read(settingsProvider).saveResponses; diff --git a/lib/providers/ui_providers.dart b/lib/providers/ui_providers.dart index 2fddb2ad..62f83f70 100644 --- a/lib/providers/ui_providers.dart +++ b/lib/providers/ui_providers.dart @@ -23,3 +23,5 @@ final nameTextFieldFocusNodeProvider = }); return focusNode; }); + +final searchQueryProvider = StateProvider((ref) => ''); diff --git a/lib/screens/home_page/collection_pane.dart b/lib/screens/home_page/collection_pane.dart index c44ed56e..abfc1f28 100644 --- a/lib/screens/home_page/collection_pane.dart +++ b/lib/screens/home_page/collection_pane.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:apidash/providers/providers.dart'; @@ -67,33 +66,44 @@ class CollectionPane extends ConsumerWidget { style: kTextStyleButton, ), ), - ], ), ), kVSpacer10, - SizedBox( - height:30, - child:SearchBar( - - onChanged: (value){ - if(value.isNotEmpty){ - ref.read(searchQueryProvider.notifier).state = value; - ref.read(collectionStateNotifierProvider.notifier) - .filterRequests(value); - } - else{ - - } - - }, - hintText: "search", - leading: Icon(Icons.search), - textStyle: MaterialStateTextStyle.resolveWith((states) => TextStyle(fontSize: 15.0)), - elevation: MaterialStateProperty.all(2.0), - )), + Container( + height: 30, + margin: const EdgeInsets.only(right: 8), + decoration: BoxDecoration( + borderRadius: kBorderRadius8, + border: Border.all( + color: Theme.of(context).colorScheme.surfaceVariant, + ), + ), + child: Row( + children: [ + kHSpacer5, + Icon( + Icons.filter_alt, + size: 18, + color: Theme.of(context).colorScheme.secondary, + ), + kHSpacer5, + Expanded( + child: RawTextField( + style: Theme.of(context).textTheme.bodyMedium, + hintText: "Filter by name or URL", + onChanged: (value) { + if (value.trim().isNotEmpty) { + ref.read(searchQueryProvider.notifier).state = value; + print(value); + } + }, + ), + ), + ], + ), + ), kVSpacer10, - const Expanded( child: RequestList(), ), @@ -134,7 +144,7 @@ class _RequestListState extends ConsumerState { final alwaysShowCollectionPaneScrollbar = ref.watch(settingsProvider .select((value) => value.alwaysShowCollectionPaneScrollbar)); - return requestItems.isNotEmpty?Scrollbar( + return Scrollbar( controller: controller, thumbVisibility: alwaysShowCollectionPaneScrollbar ? true : null, radius: const Radius.circular(12), @@ -155,7 +165,6 @@ class _RequestListState extends ConsumerState { }, itemBuilder: (context, index) { var id = requestSequence[index]; - print(requestItems[id]!); return ReorderableDragStartListener( key: ValueKey(id), @@ -170,7 +179,7 @@ class _RequestListState extends ConsumerState { ); }, ), - ):Text("data"); + ); } } diff --git a/lib/widgets/textfields.dart b/lib/widgets/textfields.dart index da68f9e2..35d3437f 100644 --- a/lib/widgets/textfields.dart +++ b/lib/widgets/textfields.dart @@ -94,14 +94,39 @@ class JsonSearchField extends StatelessWidget { @override Widget build(BuildContext context) { - return TextField( + return RawTextField( controller: controller, onChanged: onChanged, style: kCodeStyle, - decoration: const InputDecoration( + hintText: 'Search..', + ); + } +} + +class RawTextField extends StatelessWidget { + const RawTextField({ + super.key, + this.onChanged, + this.controller, + this.hintText, + this.style, + }); + + final void Function(String)? onChanged; + final TextEditingController? controller; + final String? hintText; + final TextStyle? style; + + @override + Widget build(BuildContext context) { + return TextField( + controller: controller, + onChanged: onChanged, + style: style, + decoration: InputDecoration( isDense: true, border: InputBorder.none, - hintText: 'Search..', + hintText: hintText, ), ); } From 77cb9db9a35566a7c0b543026a3c092ca2a0be55 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 24 Mar 2024 01:20:38 +0530 Subject: [PATCH 22/25] Update collection_pane.dart --- lib/screens/home_page/collection_pane.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/screens/home_page/collection_pane.dart b/lib/screens/home_page/collection_pane.dart index abfc1f28..1ba202eb 100644 --- a/lib/screens/home_page/collection_pane.dart +++ b/lib/screens/home_page/collection_pane.dart @@ -165,7 +165,6 @@ class _RequestListState extends ConsumerState { }, itemBuilder: (context, index) { var id = requestSequence[index]; - return ReorderableDragStartListener( key: ValueKey(id), index: index, From 1451ea756702f58e4dc7b9aac2d1ca542dcc37c0 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 24 Mar 2024 02:37:47 +0530 Subject: [PATCH 23/25] Update collection_pane.dart --- lib/screens/home_page/collection_pane.dart | 68 +++++++++++----------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/lib/screens/home_page/collection_pane.dart b/lib/screens/home_page/collection_pane.dart index 1ba202eb..7d89a375 100644 --- a/lib/screens/home_page/collection_pane.dart +++ b/lib/screens/home_page/collection_pane.dart @@ -93,10 +93,7 @@ class CollectionPane extends ConsumerWidget { style: Theme.of(context).textTheme.bodyMedium, hintText: "Filter by name or URL", onChanged: (value) { - if (value.trim().isNotEmpty) { - ref.read(searchQueryProvider.notifier).state = value; - print(value); - } + ref.read(searchQueryProvider.notifier).state = value; }, ), ), @@ -143,41 +140,44 @@ class _RequestListState extends ConsumerState { final requestItems = ref.watch(collectionStateNotifierProvider)!; final alwaysShowCollectionPaneScrollbar = ref.watch(settingsProvider .select((value) => value.alwaysShowCollectionPaneScrollbar)); + final filterQuery = ref.watch(searchQueryProvider).trim(); return Scrollbar( controller: controller, thumbVisibility: alwaysShowCollectionPaneScrollbar ? true : null, radius: const Radius.circular(12), - child: ReorderableListView.builder( - padding: kPr8CollectionPane, - scrollController: controller, - buildDefaultDragHandles: false, - itemCount: requestItems.length, - onReorder: (int oldIndex, int newIndex) { - if (oldIndex < newIndex) { - newIndex -= 1; - } - if (oldIndex != newIndex) { - ref - .read(collectionStateNotifierProvider.notifier) - .reorder(oldIndex, newIndex); - } - }, - itemBuilder: (context, index) { - var id = requestSequence[index]; - return ReorderableDragStartListener( - key: ValueKey(id), - index: index, - child: Padding( - padding: kP1, - child: RequestItem( - id: id, - requestModel: requestItems[id]!, - ), - ), - ); - }, - ), + child: filterQuery.isEmpty + ? ReorderableListView.builder( + padding: kPr8CollectionPane, + scrollController: controller, + buildDefaultDragHandles: false, + itemCount: requestItems.length, + onReorder: (int oldIndex, int newIndex) { + if (oldIndex < newIndex) { + newIndex -= 1; + } + if (oldIndex != newIndex) { + ref + .read(collectionStateNotifierProvider.notifier) + .reorder(oldIndex, newIndex); + } + }, + itemBuilder: (context, index) { + var id = requestSequence[index]; + return ReorderableDragStartListener( + key: ValueKey(id), + index: index, + child: Padding( + padding: kP1, + child: RequestItem( + id: id, + requestModel: requestItems[id]!, + ), + ), + ); + }, + ) + : SizedBox(), ); } } From 4243904aa97d9fbb3d5a4d701312eb343bea9d66 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 24 Mar 2024 02:56:28 +0530 Subject: [PATCH 24/25] Filtered list --- lib/screens/home_page/collection_pane.dart | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/screens/home_page/collection_pane.dart b/lib/screens/home_page/collection_pane.dart index d377e786..0ede2021 100644 --- a/lib/screens/home_page/collection_pane.dart +++ b/lib/screens/home_page/collection_pane.dart @@ -93,7 +93,8 @@ class CollectionPane extends ConsumerWidget { style: Theme.of(context).textTheme.bodyMedium, hintText: "Filter by name or URL", onChanged: (value) { - ref.read(searchQueryProvider.notifier).state = value; + ref.read(searchQueryProvider.notifier).state = + value.toLowerCase(); }, ), ), @@ -177,7 +178,24 @@ class _RequestListState extends ConsumerState { ); }, ) - : SizedBox(), + : ListView( + padding: kPe8, + controller: controller, + children: requestSequence.map((id) { + var item = requestItems[id]!; + if (item.url.toLowerCase().contains(filterQuery) || + item.name.toLowerCase().contains(filterQuery)) { + return Padding( + padding: kP1, + child: RequestItem( + id: id, + requestModel: item, + ), + ); + } + return const SizedBox(); + }).toList(), + ), ); } } From 8a8ded7d203fd00bb78374cfef6bea020278e113 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sun, 24 Mar 2024 03:18:04 +0530 Subject: [PATCH 25/25] Update tests --- test/providers/ui_providers_test.dart | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/test/providers/ui_providers_test.dart b/test/providers/ui_providers_test.dart index dde4983f..9d34619d 100644 --- a/test/providers/ui_providers_test.dart +++ b/test/providers/ui_providers_test.dart @@ -214,13 +214,13 @@ void main() { }); group("Testing selectedIdEditStateProvider", () { - testWidgets( - 'selectedIdEditStateProvider should have an initial value of null', - (tester) async { + testWidgets('It should have an initial value of null', (tester) async { await tester.pumpWidget( const ProviderScope( child: MaterialApp( - home: CollectionPane(), + home: Scaffold( + body: CollectionPane(), + ), ), ), ); @@ -237,7 +237,9 @@ void main() { await tester.pumpWidget( const ProviderScope( child: MaterialApp( - home: CollectionPane(), + home: Scaffold( + body: CollectionPane(), + ), ), ), ); @@ -267,7 +269,9 @@ void main() { await tester.pumpWidget( const ProviderScope( child: MaterialApp( - home: CollectionPane(), + home: Scaffold( + body: CollectionPane(), + ), ), ), ); @@ -303,7 +307,9 @@ void main() { await tester.pumpWidget( const ProviderScope( child: MaterialApp( - home: CollectionPane(), + home: Scaffold( + body: CollectionPane(), + ), ), ), );