diff --git a/lib/widgets/html_previewer.dart b/lib/widgets/html_previewer.dart new file mode 100644 index 000000000..060d6fab9 --- /dev/null +++ b/lib/widgets/html_previewer.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:webview_cef/webview_cef.dart'; + +class HtmlPreviewer extends StatefulWidget { + const HtmlPreviewer({super.key, required this.url}); + final String url; + + @override + _HtmlPreviewerState createState() => _HtmlPreviewerState(); +} + +class _HtmlPreviewerState extends State { + final WebViewController _controller = WebViewController(); + final _textController = TextEditingController(); + String title = ""; + Future initPlatformState() async { + _textController.text = widget.url; + await _controller.initialize(); + await _controller.loadUrl(widget.url); + _controller.setWebviewListener(WebviewEventsListener( + onTitleChanged: (t) { + setState(() { + title = t; + }); + }, + onUrlChanged: (url) { + _textController.text = url; + }, + )); + + // ignore: prefer_collection_literals + final Set jsChannels = [ + JavascriptChannel( + name: 'Print', + onMessageReceived: (JavascriptMessage message) { + _controller.sendJavaScriptChannelCallBack( + false, + "{'code':'200','message':'print succeed!'}", + message.callbackId, + message.frameId); + }), + ].toSet(); + + await _controller.setJavaScriptChannels(jsChannels); + + await _controller.executeJavaScript("function abc(e){console.log(e)}"); + + if (!mounted) return; + } + + @override + void initState() { + super.initState(); + initPlatformState(); + } + + @override + Widget build(BuildContext context) { + return Expanded(child: WebView(_controller)); + } +} diff --git a/lib/widgets/previewer.dart b/lib/widgets/previewer.dart index dd2f51869..46130ace5 100644 --- a/lib/widgets/previewer.dart +++ b/lib/widgets/previewer.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:apidash/widgets/html_previewer.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:printing/printing.dart'; diff --git a/lib/widgets/response_widgets.dart b/lib/widgets/response_widgets.dart index 539a2a341..bba43bc03 100644 --- a/lib/widgets/response_widgets.dart +++ b/lib/widgets/response_widgets.dart @@ -1,11 +1,16 @@ +import 'package:apidash/providers/collection_providers.dart'; +import 'package:apidash/providers/providers.dart'; +import 'package:apidash/widgets/html_previewer.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:http_parser/http_parser.dart'; import 'package:lottie/lottie.dart'; 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:provider/provider.dart'; class NotSentWidget extends StatelessWidget { const NotSentWidget({super.key}); @@ -343,7 +348,7 @@ class ResponseBody extends StatelessWidget { } } -class BodySuccess extends StatefulWidget { +class BodySuccess extends ConsumerStatefulWidget { const BodySuccess( {super.key, required this.mediaType, @@ -359,14 +364,20 @@ class BodySuccess extends StatefulWidget { final String? formattedBody; final String? highlightLanguage; @override - State createState() => _BodySuccessState(); + ConsumerState createState() => _BodySuccessState(); } -class _BodySuccessState extends State { +class _BodySuccessState extends ConsumerState { int segmentIdx = 0; + @override + void initState() { + super.initState(); + ref.read(selectedRequestModelProvider); + } @override Widget build(BuildContext context) { + final String url = ref.watch(selectedRequestModelProvider)!.url; var currentSeg = widget.options[segmentIdx]; var codeTheme = Theme.of(context).brightness == Brightness.light ? kLightCodeTheme @@ -388,55 +399,64 @@ class _BodySuccessState extends State { widget.options.length, constraints.maxWidth, ); + final isHtmlResponse = widget.mediaType.type == kTypeText && + widget.mediaType.subtype == kSubTypeHtml; return Padding( padding: kP10, child: Column( children: [ - Row( - children: [ - (widget.options == kRawBodyViewOptions) - ? const SizedBox() - : SegmentedButton( - style: const ButtonStyle( - padding: MaterialStatePropertyAll( - EdgeInsets.symmetric( - horizontal: 8, - ), + !isHtmlResponse + ? Column( + children: [ + Row( + children: [ + (widget.options == kRawBodyViewOptions) + ? const SizedBox() + : SegmentedButton( + style: const ButtonStyle( + padding: MaterialStatePropertyAll( + EdgeInsets.symmetric( + horizontal: 8, + ), + ), + ), + selectedIcon: Icon(currentSeg.icon), + segments: widget.options + .map>( + (e) => + ButtonSegment( + value: e, + label: Text(e.label), + icon: Icon(e.icon), + ), + ) + .toList(), + selected: {currentSeg}, + onSelectionChanged: (newSelection) { + setState(() { + segmentIdx = widget.options + .indexOf(newSelection.first); + }); + }, + ), + const Spacer(), + kCodeRawBodyViewOptions.contains(currentSeg) + ? CopyButton( + toCopy: widget.formattedBody ?? widget.body, + showLabel: showLabel, + ) + : const SizedBox(), + SaveInDownloadsButton( + content: widget.bytes, + mimeType: widget.mediaType.mimeType, + showLabel: showLabel, ), - ), - selectedIcon: Icon(currentSeg.icon), - segments: widget.options - .map>( - (e) => ButtonSegment( - value: e, - label: Text(e.label), - icon: Icon(e.icon), - ), - ) - .toList(), - selected: {currentSeg}, - onSelectionChanged: (newSelection) { - setState(() { - segmentIdx = - widget.options.indexOf(newSelection.first); - }); - }, + ], ), - const Spacer(), - kCodeRawBodyViewOptions.contains(currentSeg) - ? CopyButton( - toCopy: widget.formattedBody ?? widget.body, - showLabel: showLabel, - ) - : const SizedBox(), - SaveInDownloadsButton( - content: widget.bytes, - mimeType: widget.mediaType.mimeType, - showLabel: showLabel, - ), - ], - ), - kVSpacer10, + kVSpacer10 + ], + ) + : Container(), switch (currentSeg) { ResponseBodyView.preview || ResponseBodyView.none => Expanded( child: Container( @@ -457,12 +477,14 @@ class _BodySuccessState extends State { width: double.maxFinite, padding: kP8, decoration: textContainerdecoration, - child: CodePreviewer( - code: widget.formattedBody ?? widget.body, - theme: codeTheme, - language: widget.highlightLanguage, - textStyle: kCodeStyle, - ), + child: !isHtmlResponse + ? CodePreviewer( + code: widget.formattedBody ?? widget.body, + theme: codeTheme, + language: widget.highlightLanguage, + textStyle: kCodeStyle, + ) + : HtmlPreviewer(url: url), ), ), ResponseBodyView.raw => Expanded( diff --git a/pubspec.lock b/pubspec.lock index 4125ed984..fd4fcfc4e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1326,6 +1326,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + webview_cef: + dependency: "direct main" + description: + name: webview_cef + sha256: a3e7a8e54ea612324be47359601808af9b9cdc57bdb96056b88394c345e3c3c6 + url: "https://pub.dev" + source: hosted + version: "0.1.0" + webview_flutter: + dependency: "direct main" + description: + name: webview_flutter + sha256: "25e1b6e839e8cbfbd708abc6f85ed09d1727e24e08e08c6b8590d7c65c9a8932" + url: "https://pub.dev" + source: hosted + version: "4.7.0" + webview_flutter_android: + dependency: transitive + description: + name: webview_flutter_android + sha256: "3e5f4e9d818086b0d01a66fb1ff9cc72ab0cc58c71980e3d3661c5685ea0efb0" + url: "https://pub.dev" + source: hosted + version: "3.15.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: "9bf168bccdf179ce90450b5f37e36fe263f591c9338828d6bf09b6f8d0f57f86" + url: "https://pub.dev" + source: hosted + version: "3.12.0" win32: dependency: transitive description: @@ -1376,5 +1416,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.19.0" + dart: ">=3.2.3 <4.0.0" + flutter: ">=3.16.6" diff --git a/pubspec.yaml b/pubspec.yaml index ab250f26f..39f436663 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -56,6 +56,8 @@ dependencies: dart_style: ^2.3.4 json_text_field: ^1.1.0 csv: ^5.1.1 + webview_cef: ^0.1.0 + webview_flutter: ^4.7.0 dev_dependencies: flutter_test: