response pane widgets

This commit is contained in:
Ankit Mahato
2023-03-11 17:37:16 +05:30
parent eba7bbac44
commit b10ecd7708
6 changed files with 335 additions and 198 deletions

View File

@ -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<ResponseViewer> createState() => _ResponseViewerState();
ConsumerState<ResponseDetails> createState() => _ResponseDetailsState();
}
class _ResponseViewerState extends ConsumerState<ResponseViewer> {
class _ResponseDetailsState extends ConsumerState<ResponseDetails> {
@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(),
),
],
);
}
}

View File

@ -21,7 +21,7 @@ class _ResponsePaneState extends ConsumerState<ResponsePane> {
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<ResponsePane> {
}
if (responseStatus == -1) {
return ErrorMessage(message: message);
} else {
return const ResponseViewer();
}
return const ResponseDetails();
}
@override

View File

@ -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<ResponseBody> createState() => _ResponseBodyState();
}
class _ResponseBodyState extends ConsumerState<ResponseBody> {
@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),
],
),
),
);
}
}

View File

@ -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<ResponseHeaders> createState() => _ResponseHeadersState();
}
class _ResponseHeadersState extends ConsumerState<ResponseHeaders> {
@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,
),
],
),
);
}
}

View File

@ -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<ResponseTabs> createState() => _ResponseTabsState();
}
class _ResponseTabsState extends ConsumerState<ResponseTabs>
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();
}
}

18
lib/widgets/jsonview.dart Normal file
View File

@ -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,
),
);