From b10ecd7708a2e46034a10e095908fcdb23620664 Mon Sep 17 00:00:00 2001 From: Ankit Mahato Date: Sat, 11 Mar 2023 17:37:16 +0530 Subject: [PATCH] response pane widgets --- .../response_pane/response_details.dart | 247 ++++-------------- .../response_pane/response_pane.dart | 5 +- .../response_tabs/response_body.dart | 73 ++++++ .../response_tabs/response_headers.dart | 106 ++++++++ .../response_tabs/response_tabs.dart | 84 ++++++ lib/widgets/jsonview.dart | 18 ++ 6 files changed, 335 insertions(+), 198 deletions(-) create mode 100644 lib/screens/home_page/editor_pane/details_card/response_pane/response_tabs/response_body.dart create mode 100644 lib/screens/home_page/editor_pane/details_card/response_pane/response_tabs/response_headers.dart create mode 100644 lib/screens/home_page/editor_pane/details_card/response_pane/response_tabs/response_tabs.dart create mode 100644 lib/widgets/jsonview.dart diff --git a/lib/screens/home_page/editor_pane/details_card/response_pane/response_details.dart b/lib/screens/home_page/editor_pane/details_card/response_pane/response_details.dart index 5382489a..45a55aa2 100644 --- a/lib/screens/home_page/editor_pane/details_card/response_pane/response_details.dart +++ b/lib/screens/home_page/editor_pane/details_card/response_pane/response_details.dart @@ -1,217 +1,74 @@ -import 'dart:convert'; -import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_json_view/flutter_json_view.dart'; import 'package:apidash/providers/providers.dart'; -import 'package:apidash/models/models.dart'; import 'package:apidash/utils/utils.dart'; import 'package:apidash/consts.dart'; +import 'response_tabs/response_tabs.dart'; -final jsonViewTheme = JsonViewTheme( - defaultTextStyle: codeStyle, - viewType: JsonViewType.collapsible, - backgroundColor: colorBg, - stringStyle: const TextStyle(color: Colors.brown), - closeIcon: const Icon( - Icons.arrow_drop_up, - size: 18, - ), - openIcon: const Icon( - Icons.arrow_drop_down, - size: 18, - ), -); - -class ResponseViewer extends ConsumerStatefulWidget { - const ResponseViewer({super.key}); +class ResponseDetails extends ConsumerStatefulWidget { + const ResponseDetails({super.key}); @override - ConsumerState createState() => _ResponseViewerState(); + ConsumerState createState() => _ResponseDetailsState(); } -class _ResponseViewerState extends ConsumerState { +class _ResponseDetailsState extends ConsumerState { @override Widget build(BuildContext context) { final activeId = ref.watch(activeIdStateProvider); - final sentRequestId = ref.watch(sentRequestIdStateProvider); - final collection = ref.watch(collectionStateNotifierProvider); + final collection = ref.read(collectionStateNotifierProvider); final idIdx = collection.indexWhere((m) => m.id == activeId); final responseStatus = collection[idIdx].responseStatus; final message = collection[idIdx].message; - return Container(); - } -} - -class ResponseiViewer extends StatelessWidget { - final int statusCode; - final String? message; - final ResponseModel responseModel; - - const ResponseiViewer({ - super.key, - required this.statusCode, - required this.message, - required this.responseModel, - }); - - @override - Widget build(BuildContext context) { - var requestHeaders = responseModel.requestHeaders ?? {}; - var responseHeaders = responseModel.headers ?? {}; - var body = responseModel.body ?? ''; - return Padding( - padding: p10, - child: SingleChildScrollView( - child: Column( - children: [ - Row( - children: const [ - Text( - "Response", - style: textStyleButton, - ), - ], + final responseModel = collection[idIdx].responseModel; + return Column( + children: [ + Row( + children: const [ + Text( + "Response", + style: textStyleButton, ), - const SizedBox(height: 5), - Row( - children: [ - SizedBox( - width: 50, - child: Text( - statusCode.toString(), - style: codeStyle.copyWith( - color: getResponseStatusCodeColor(statusCode), - fontWeight: FontWeight.bold, - ), - ), - ), - Expanded( - child: Text( - message ?? "", - style: codeStyle.copyWith( - color: getResponseStatusCodeColor(statusCode), - fontWeight: FontWeight.bold, - ), - ), - ), - SizedBox( - width: 100, - child: Text( - humanizeDuration(responseModel.time), - style: codeStyle.copyWith( - color: getResponseStatusCodeColor(statusCode), - fontWeight: FontWeight.bold, - ), - ), - ), - ], - ), - const SizedBox(height: 5), - Row( - children: [ - Expanded( - child: Text( - "Request Headers (${requestHeaders.length} items)", - style: codeStyle, - ), - ), - if (requestHeaders.isNotEmpty) - TextButton( - onPressed: () async { - await Clipboard.setData( - ClipboardData(text: json.encode(requestHeaders))); - }, - child: Row( - children: const [ - Icon( - Icons.content_copy, - size: 20, - ), - Text("Copy") - ], - ), - ), - ], - ), - if (requestHeaders.isNotEmpty) const SizedBox(height: 5), - if (requestHeaders.isNotEmpty) - JsonView.map( - requestHeaders, - theme: jsonViewTheme, - ), - const SizedBox(height: 5), - Row( - children: [ - Expanded( - child: Text( - "Response Headers (${responseHeaders.length} items)", - style: codeStyle, - ), - ), - if (responseHeaders.isNotEmpty) - TextButton( - onPressed: () async { - await Clipboard.setData( - ClipboardData(text: json.encode(responseHeaders))); - }, - child: Row( - children: const [ - Icon( - Icons.content_copy, - size: 20, - ), - Text("Copy") - ], - ), - ), - ], - ), - if (responseHeaders.isNotEmpty) const SizedBox(height: 5), - if (responseHeaders.isNotEmpty) - JsonView.map( - responseHeaders, - theme: jsonViewTheme, - ), - const SizedBox(height: 5), - Row( - children: [ - Expanded( - child: Text( - "Body ${body.isEmpty ? '(empty)' : ''}", - style: codeStyle, - ), - ), - if (body.isNotEmpty) - TextButton( - onPressed: () async { - await Clipboard.setData(ClipboardData(text: body)); - }, - child: Row( - children: const [ - Icon( - Icons.content_copy, - size: 20, - ), - Text("Copy") - ], - ), - ), - ], - ), - const SizedBox(height: 5), - if (body.isNotEmpty && - responseModel.contentType!.startsWith(JSON_MIMETYPE)) - JsonView.string( - body, - theme: jsonViewTheme, - ), - if (body.isNotEmpty && - responseModel.contentType!.startsWith("text/")) - Text(body), ], ), - ), + const SizedBox(height: 5), + Row( + children: [ + SizedBox( + width: 50, + child: Text( + "$responseStatus", + style: codeStyle.copyWith( + color: getResponseStatusCodeColor(responseStatus), + fontWeight: FontWeight.bold, + ), + ), + ), + Expanded( + child: Text( + message ?? "", + style: codeStyle.copyWith( + color: getResponseStatusCodeColor(responseStatus), + fontWeight: FontWeight.bold, + ), + ), + ), + SizedBox( + width: 100, + child: Text( + humanizeDuration(responseModel!.time), + style: codeStyle.copyWith( + color: getResponseStatusCodeColor(responseStatus), + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + const Expanded( + child: ResponseTabs(), + ), + ], ); } } diff --git a/lib/screens/home_page/editor_pane/details_card/response_pane/response_pane.dart b/lib/screens/home_page/editor_pane/details_card/response_pane/response_pane.dart index 6a12333e..e4228ad2 100644 --- a/lib/screens/home_page/editor_pane/details_card/response_pane/response_pane.dart +++ b/lib/screens/home_page/editor_pane/details_card/response_pane/response_pane.dart @@ -21,7 +21,7 @@ class _ResponsePaneState extends ConsumerState { Widget build(BuildContext context) { final activeId = ref.watch(activeIdStateProvider); final sentRequestId = ref.watch(sentRequestIdStateProvider); - final collection = ref.watch(collectionStateNotifierProvider); + final collection = ref.read(collectionStateNotifierProvider); final idIdx = collection.indexWhere((m) => m.id == activeId); final responseStatus = collection[idIdx].responseStatus; final message = collection[idIdx].message; @@ -33,9 +33,8 @@ class _ResponsePaneState extends ConsumerState { } if (responseStatus == -1) { return ErrorMessage(message: message); - } else { - return const ResponseViewer(); } + return const ResponseDetails(); } @override diff --git a/lib/screens/home_page/editor_pane/details_card/response_pane/response_tabs/response_body.dart b/lib/screens/home_page/editor_pane/details_card/response_pane/response_tabs/response_body.dart new file mode 100644 index 00000000..b789ec90 --- /dev/null +++ b/lib/screens/home_page/editor_pane/details_card/response_pane/response_tabs/response_body.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_json_view/flutter_json_view.dart'; +import 'package:apidash/providers/providers.dart'; +import 'package:apidash/widgets/jsonview.dart'; +import 'package:apidash/consts.dart'; + +class ResponseBody extends ConsumerStatefulWidget { + const ResponseBody({super.key}); + + @override + ConsumerState createState() => _ResponseBodyState(); +} + +class _ResponseBodyState extends ConsumerState { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + final activeId = ref.watch(activeIdStateProvider); + final collection = ref.watch(collectionStateNotifierProvider); + final idIdx = collection.indexWhere((m) => m.id == activeId); + final responseModel = collection[idIdx].responseModel; + var body = responseModel?.body ?? ''; + var contentType = responseModel?.contentType ?? ""; + return Padding( + padding: p10, + child: SingleChildScrollView( + child: Column( + children: [ + const SizedBox(height: 5), + Row( + children: [ + Expanded( + child: Text( + "Body ${body.isEmpty ? '(empty)' : ''}", + style: codeStyle, + ), + ), + if (body.isNotEmpty) + TextButton( + onPressed: () async { + await Clipboard.setData(ClipboardData(text: body)); + }, + child: Row( + children: const [ + Icon( + Icons.content_copy, + size: 20, + ), + Text("Copy") + ], + ), + ), + ], + ), + const SizedBox(height: 5), + if (body.isNotEmpty && contentType.startsWith(JSON_MIMETYPE)) + JsonView.string( + body, + theme: jsonViewTheme, + ), + if (body.isNotEmpty && contentType.startsWith("text/")) Text(body), + ], + ), + ), + ); + } +} diff --git a/lib/screens/home_page/editor_pane/details_card/response_pane/response_tabs/response_headers.dart b/lib/screens/home_page/editor_pane/details_card/response_pane/response_tabs/response_headers.dart new file mode 100644 index 00000000..107efdd8 --- /dev/null +++ b/lib/screens/home_page/editor_pane/details_card/response_pane/response_tabs/response_headers.dart @@ -0,0 +1,106 @@ +import 'dart:convert'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_json_view/flutter_json_view.dart'; +import 'package:apidash/providers/providers.dart'; +import 'package:apidash/widgets/jsonview.dart'; +import 'package:apidash/consts.dart'; + +class ResponseHeaders extends ConsumerStatefulWidget { + const ResponseHeaders({super.key}); + + @override + ConsumerState createState() => _ResponseHeadersState(); +} + +class _ResponseHeadersState extends ConsumerState { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + final activeId = ref.watch(activeIdStateProvider); + final collection = ref.watch(collectionStateNotifierProvider); + final idIdx = collection.indexWhere((m) => m.id == activeId); + final requestHeaders = + collection[idIdx].responseModel?.requestHeaders ?? {}; + final responseHeaders = collection[idIdx].responseModel?.headers ?? {}; + return Container( + decoration: tableContainerDecoration, + margin: p5, + child: ListView( + children: [ + const SizedBox(height: 5), + Row( + children: [ + Expanded( + child: Text( + "Request Headers (${requestHeaders.length} items)", + style: codeStyle, + ), + ), + if (requestHeaders.isNotEmpty) + TextButton( + onPressed: () async { + await Clipboard.setData( + ClipboardData(text: json.encode(requestHeaders))); + }, + child: Row( + children: const [ + Icon( + Icons.content_copy, + size: 20, + ), + Text("Copy") + ], + ), + ), + ], + ), + if (requestHeaders.isNotEmpty) const SizedBox(height: 5), + if (requestHeaders.isNotEmpty) + JsonView.map( + requestHeaders, + theme: jsonViewTheme, + ), + const SizedBox(height: 5), + Row( + children: [ + Expanded( + child: Text( + "Response Headers (${responseHeaders.length} items)", + style: codeStyle, + ), + ), + if (responseHeaders.isNotEmpty) + TextButton( + onPressed: () async { + await Clipboard.setData( + ClipboardData(text: json.encode(responseHeaders))); + }, + child: Row( + children: const [ + Icon( + Icons.content_copy, + size: 20, + ), + Text("Copy") + ], + ), + ), + ], + ), + if (responseHeaders.isNotEmpty) const SizedBox(height: 5), + if (responseHeaders.isNotEmpty) + JsonView.map( + responseHeaders, + theme: jsonViewTheme, + ), + ], + ), + ); + } +} diff --git a/lib/screens/home_page/editor_pane/details_card/response_pane/response_tabs/response_tabs.dart b/lib/screens/home_page/editor_pane/details_card/response_pane/response_tabs/response_tabs.dart new file mode 100644 index 00000000..cdb9f13a --- /dev/null +++ b/lib/screens/home_page/editor_pane/details_card/response_pane/response_tabs/response_tabs.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:apidash/providers/providers.dart'; +import 'package:apidash/consts.dart'; +import 'response_headers.dart'; +import 'response_body.dart'; + +class ResponseTabs extends ConsumerStatefulWidget { + const ResponseTabs({super.key}); + + @override + ConsumerState createState() => _ResponseTabsState(); +} + +class _ResponseTabsState extends ConsumerState + with TickerProviderStateMixin { + late final TabController _controller; + + @override + void initState() { + super.initState(); + _controller = TabController( + length: 2, + animationDuration: tabAnimationDuration, + vsync: this, + ); + } + + @override + Widget build(BuildContext context) { + final activeId = ref.watch(activeIdStateProvider); + return Column( + children: [ + TabBar( + key: Key(activeId!), + controller: _controller, + overlayColor: colorTransparent, + onTap: (index) {}, + tabs: const [ + SizedBox( + height: 45, + child: Center( + child: Text( + 'Body', + textAlign: TextAlign.center, + overflow: TextOverflow.fade, + softWrap: false, + style: textStyleButton, + ), + ), + ), + SizedBox( + height: 45, + child: Center( + child: Text( + 'Headers', + textAlign: TextAlign.center, + overflow: TextOverflow.fade, + style: textStyleButton, + ), + ), + ), + ], + ), + Expanded( + child: TabBarView( + controller: _controller, + physics: const NeverScrollableScrollPhysics(), + children: const [ + ResponseBody(), + ResponseHeaders(), + ], + ), + ), + ], + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} diff --git a/lib/widgets/jsonview.dart b/lib/widgets/jsonview.dart new file mode 100644 index 00000000..d83bce5a --- /dev/null +++ b/lib/widgets/jsonview.dart @@ -0,0 +1,18 @@ +import 'package:flutter_json_view/flutter_json_view.dart'; +import 'package:apidash/consts.dart'; +import 'package:flutter/material.dart'; + +final jsonViewTheme = JsonViewTheme( + defaultTextStyle: codeStyle, + viewType: JsonViewType.collapsible, + backgroundColor: colorBg, + stringStyle: const TextStyle(color: Colors.brown), + closeIcon: const Icon( + Icons.arrow_drop_up, + size: 18, + ), + openIcon: const Icon( + Icons.arrow_drop_down, + size: 18, + ), +);