From f4106e54edacf1752bfcb8f79264b9f82ed17796 Mon Sep 17 00:00:00 2001 From: chetanr250 <=> Date: Tue, 15 Oct 2024 13:40:24 +0530 Subject: [PATCH 1/2] feat: Implement HTML rendering with browser interactivity using webview_flutter: #155: --- lib/consts.dart | 1 + .../details_card/response_pane.dart | 14 ++++++++ lib/widgets/response_widgets.dart | 33 +++++++++++++++++- pubspec.lock | 34 ++++++++++++++++++- pubspec.yaml | 1 + 5 files changed, 81 insertions(+), 2 deletions(-) diff --git a/lib/consts.dart b/lib/consts.dart index e47750693..4bf40e331 100644 --- a/lib/consts.dart +++ b/lib/consts.dart @@ -773,6 +773,7 @@ const kLabelAddFormField = "Add Form Field"; const kLabelNotSent = "Not Sent"; const kLabelResponse = "Response"; const kLabelResponseBody = "Response Body"; +const kLabelPreview = "Preview"; const kTooltipClearResponse = "Clear Response"; const kHeaderRow = ["Header Name", "Header Value"]; const kLabelRequestHeaders = "Request Headers"; 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 0ca4c1a96..c770207b6 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 @@ -77,6 +77,7 @@ class ResponseTabs extends ConsumerWidget { children: const [ ResponseBodyTab(), ResponseHeadersTab(), + ResponsePreviewTab(), ], ); } @@ -111,3 +112,16 @@ class ResponseHeadersTab extends ConsumerWidget { ); } } + +class ResponsePreviewTab extends ConsumerWidget { + const ResponsePreviewTab({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final selectedRequestModel = ref.watch(selectedRequestModelProvider); + final responseModel = selectedRequestModel?.httpResponseModel; + return ResponsePreview( + responseModel: responseModel, + ); + } +} diff --git a/lib/widgets/response_widgets.dart b/lib/widgets/response_widgets.dart index 7b269fc1b..eb0aa7b76 100644 --- a/lib/widgets/response_widgets.dart +++ b/lib/widgets/response_widgets.dart @@ -7,6 +7,7 @@ import 'package:apidash/utils/utils.dart'; import 'package:apidash/widgets/widgets.dart'; import 'package:apidash/models/models.dart'; import 'package:apidash/consts.dart'; +import 'package:webview_flutter/webview_flutter.dart'; class NotSentWidget extends StatelessWidget { const NotSentWidget({super.key}); @@ -193,7 +194,7 @@ class _ResponseTabViewState extends State void initState() { super.initState(); _controller = TabController( - length: 2, + length: 3, animationDuration: kTabAnimationDuration, vsync: this, ); @@ -216,6 +217,9 @@ class _ResponseTabViewState extends State TabLabel( text: kLabelHeaders, ), + TabLabel( + text: kLabelPreview, + ), ], ), Expanded( @@ -518,3 +522,30 @@ class _BodySuccessState extends State { ); } } + +class ResponsePreview extends StatelessWidget { + const ResponsePreview({super.key, required this.responseModel}); + final responseModel; + + @override + Widget build(BuildContext context) { + final controller = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setNavigationDelegate( + NavigationDelegate( + onWebResourceError: (WebResourceError error) { + ErrorMessage(message: error.description); + }, + ), + ); + + // Load HTML content + WidgetsBinding.instance.addPostFrameCallback((_) { + controller.loadHtmlString(responseModel!.body!); + }); + + return Scaffold( + body: WebViewWidget(controller: controller), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 8f73cfb23..6b5a77779 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1700,6 +1700,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + webview_flutter: + dependency: "direct main" + description: + name: webview_flutter + sha256: "889a0a678e7c793c308c68739996227c9661590605e70b1f6cf6b9a6634f7aec" + url: "https://pub.dev" + source: hosted + version: "4.10.0" + webview_flutter_android: + dependency: transitive + description: + name: webview_flutter_android + sha256: "74693a212d990b32e0b7055d27db973a18abf31c53942063948cdfaaef9787ba" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + webview_flutter_platform_interface: + dependency: transitive + description: + name: webview_flutter_platform_interface + sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d + url: "https://pub.dev" + source: hosted + version: "2.10.0" + webview_flutter_wkwebview: + dependency: transitive + description: + name: webview_flutter_wkwebview + sha256: d4034901d96357beb1b6717ebf7d583c88e40cfc6eb85fe76dd1bf0979a9f251 + url: "https://pub.dev" + source: hosted + version: "3.16.0" win32: dependency: transitive description: @@ -1750,5 +1782,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.5.0-259.0.dev <3.999.0" + dart: ">=3.5.0 <3.999.0" flutter: ">=3.24.2" diff --git a/pubspec.yaml b/pubspec.yaml index 66dde1e8f..5b0d79b01 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -75,6 +75,7 @@ dependencies: url: https://github.com/google/flutter-desktop-embedding.git path: plugins/window_size xml: ^6.3.0 + webview_flutter: ^4.10.0 dependency_overrides: extended_text_field: ^16.0.0 From 28315d664e2c6a2ef32521e9737ea56bcd6a2b61 Mon Sep 17 00:00:00 2001 From: chetanr250 <=> Date: Tue, 15 Oct 2024 15:49:35 +0530 Subject: [PATCH 2/2] Changes made in test files --- .../history_widgets/his_response_pane.dart | 3 +++ lib/widgets/response_widgets.dart | 16 ++++++++++++++-- test/widgets/response_widgets_test.dart | 3 ++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/screens/history/history_widgets/his_response_pane.dart b/lib/screens/history/history_widgets/his_response_pane.dart index f90390cb8..6d20d8aeb 100644 --- a/lib/screens/history/history_widgets/his_response_pane.dart +++ b/lib/screens/history/history_widgets/his_response_pane.dart @@ -37,6 +37,9 @@ class HistoryResponsePane extends ConsumerWidget { requestHeaders: historyHttpResponseModel?.requestHeaders ?? {}, ), + ResponsePreview( + responseModel: historyHttpResponseModel ?? {}, + ), ], ), ), diff --git a/lib/widgets/response_widgets.dart b/lib/widgets/response_widgets.dart index eb0aa7b76..dc46a050c 100644 --- a/lib/widgets/response_widgets.dart +++ b/lib/widgets/response_widgets.dart @@ -529,6 +529,13 @@ class ResponsePreview extends StatelessWidget { @override Widget build(BuildContext context) { + if (responseModel == {} || + responseModel == null || + responseModel.body == null) { + return const ErrorMessage( + message: '$kNullResponseModelError $kUnexpectedRaiseIssue'); + } + final controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..setNavigationDelegate( @@ -538,8 +545,13 @@ class ResponsePreview extends StatelessWidget { }, ), ); - - // Load HTML content + controller.setNavigationDelegate( + NavigationDelegate( + onWebResourceError: (WebResourceError error) { + ErrorMessage(message: error.description); + }, + ), + ); WidgetsBinding.instance.addPostFrameCallback((_) { controller.loadHtmlString(responseModel!.body!); }); diff --git a/test/widgets/response_widgets_test.dart b/test/widgets/response_widgets_test.dart index 74df9a1bd..f617c4993 100644 --- a/test/widgets/response_widgets_test.dart +++ b/test/widgets/response_widgets_test.dart @@ -88,7 +88,8 @@ void main() { theme: kThemeDataLight, home: const Scaffold( body: ResponseTabView( - selectedId: '1', children: [Text('first'), Text('second')]), + selectedId: '1', + children: [Text('first'), Text('second'), Text('third')]), ), ), );