Fully functional prototype with final UI design

This commit is contained in:
Ankit Mahato
2023-03-05 14:51:15 +05:30
parent aa1567851c
commit a9810ef7c2
16 changed files with 800 additions and 213 deletions

View File

@ -1 +1,11 @@
include: package:flutter_lints/flutter.yaml
analyzer:
errors:
invalid_annotation_target: ignore
enable-experiment:
- records
linter:
rules:
constant_identifier_names: false

View File

@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/models.dart';
import '../providers/providers.dart';
import '../utils/utils.dart';
import 'styles.dart';
import '../consts.dart';
class CollectionPane extends ConsumerStatefulWidget {
@ -38,7 +39,10 @@ class _CollectionPaneState extends ConsumerState<CollectionPane> {
.read(activeItemIdStateProvider.notifier)
.update((state) => newId);
},
child: const Text('+ New'),
child: const Text(
'+ New',
style: textStyleButton,
),
),
],
),
@ -120,12 +124,9 @@ class RequestItem extends ConsumerStatefulWidget {
}
class _RequestItemState extends ConsumerState<RequestItem> {
late Color _color;
@override
void initState() {
super.initState();
_color = Colors.grey.shade50;
}
@override
@ -133,11 +134,11 @@ class _RequestItemState extends ConsumerState<RequestItem> {
final activeRequest = ref.watch(activeItemIdStateProvider);
bool isActiveId = activeRequest == widget.id;
return Material(
borderRadius: BorderRadius.circular(10.0),
borderRadius: borderRadius10,
elevation: isActiveId ? 2 : 0,
color: isActiveId ? Colors.grey.shade300 : _color,
color: isActiveId ? colorGrey300 : colorGrey50,
child: InkWell(
borderRadius: BorderRadius.circular(10.0),
borderRadius: borderRadius10,
onTap: () {
ref
.read(activeItemIdStateProvider.notifier)

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../providers/providers.dart';
import '../styles.dart';
import '../../consts.dart';
class EditRequestBody extends StatefulWidget {
@ -13,23 +14,92 @@ class EditRequestBody extends StatefulWidget {
class _EditRequestBodyState extends State<EditRequestBody> {
@override
Widget build(BuildContext context) {
return Column(
return Container(
decoration: tableContainerDecoration,
margin: p5,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(10),
padding: p10,
child: Row(
children: [
children: const [
Text(
"Select Content Type:",
//style: Theme.of(context).textTheme.titleMedium,
),
SizedBox(
child: DropdownButtonBodyContentType(),
height: 30,
)
child: DropdownButtonBodyContentType(),
),
],
),
),
Expanded(
const Divider(),
const Expanded(
child: Padding(
padding: p10,
child: TextFieldEditor(),
),
)
],
),
);
}
}
class DropdownButtonBodyContentType extends ConsumerStatefulWidget {
const DropdownButtonBodyContentType({
super.key,
});
@override
ConsumerState<DropdownButtonBodyContentType> createState() =>
_DropdownButtonBodyContentTypeState();
}
class _DropdownButtonBodyContentTypeState
extends ConsumerState<DropdownButtonBodyContentType> {
@override
Widget build(BuildContext context) {
final activeId = ref.watch(activeItemIdStateProvider);
final collection = ref.read(collectionStateNotifierProvider);
final idIdx = collection.indexWhere((m) => m.id == activeId);
final requestBodyContentType = ref.watch(collectionStateNotifierProvider
.select((value) => value[idIdx].requestBodyContentType));
return DropdownButton<ContentType>(
focusColor: colorGrey50,
value: requestBodyContentType,
icon: const Icon(
Icons.unfold_more_rounded,
size: 16,
),
elevation: 4,
style: codeStyle.copyWith(
color: Theme.of(context)
.colorScheme
.primary), //Theme.of(context).textTheme.bodyMedium,
underline: Container(
height: 0,
),
onChanged: (ContentType? value) {
ref
.read(collectionStateNotifierProvider.notifier)
.update(activeId!, requestBodyContentType: value);
},
borderRadius: borderRadius10,
items: ContentType.values
.map<DropdownMenuItem<ContentType>>((ContentType value) {
return DropdownMenuItem<ContentType>(
value: value,
child: Padding(
padding: const EdgeInsets.only(left: 8),
child: Text(
value.name,
style: textStyleButton,
),
),
);
}).toList(),
);
}
}
@ -61,9 +131,10 @@ class _TextFieldEditorState extends ConsumerState<TextFieldEditor> {
.read(collectionStateNotifierProvider.notifier)
.update(activeId, requestBody: value);
},
style: codeStyle,
decoration: InputDecoration(
hintText: "Enter body",
hintStyle: TextStyle(color: Colors.grey.shade500),
hintText: "Enter content (body)",
hintStyle: codeStyle.copyWith(color: colorGrey500),
border: InputBorder.none,
),
keyboardType: TextInputType.multiline,
@ -72,55 +143,3 @@ class _TextFieldEditorState extends ConsumerState<TextFieldEditor> {
);
}
}
class DropdownButtonBodyContentType extends ConsumerStatefulWidget {
const DropdownButtonBodyContentType({
super.key,
});
@override
ConsumerState<DropdownButtonBodyContentType> createState() =>
_DropdownButtonBodyContentTypeState();
}
class _DropdownButtonBodyContentTypeState
extends ConsumerState<DropdownButtonBodyContentType> {
@override
Widget build(BuildContext context) {
final activeId = ref.watch(activeItemIdStateProvider);
final reqestModel = ref
.read(collectionStateNotifierProvider.notifier)
.getRequestModel(activeId!);
return DropdownButton<ContentType>(
focusColor: Colors.white,
value: reqestModel.requestBodyContentType,
icon: const Icon(
Icons.unfold_more_rounded,
size: 16,
),
elevation: 4,
underline: Container(
height: 0,
//color: Colors.deepPurpleAccent,
),
onChanged: (ContentType? value) {
ref
.read(collectionStateNotifierProvider.notifier)
.update(activeId!, requestBodyContentType: value);
},
borderRadius: BorderRadius.circular(10),
items: ContentType.values
.map<DropdownMenuItem<ContentType>>((ContentType value) {
return DropdownMenuItem<ContentType>(
value: value,
child: Padding(
padding: const EdgeInsets.only(left: 8),
child: Text(
value.name,
),
),
);
}).toList(),
);
}
}

View File

@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:multi_split_view/multi_split_view.dart';
import 'request_pane.dart';
import 'response_pane.dart';
@ -30,8 +29,8 @@ class _EditorPaneRequestDetailsCardState
data: MultiSplitViewThemeData(
dividerThickness: 3,
dividerPainter: DividerPainters.background(
color: Colors.grey.shade200,
highlightedColor: Colors.grey.shade400,
color: colorGrey200,
highlightedColor: colorGrey400,
animationEnabled: false,
),
),

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:tab_container/tab_container.dart';
import '../../providers/providers.dart';
import '../styles.dart';
import 'tables.dart';
import 'body_editor.dart';
@ -28,28 +29,41 @@ class _EditRequestPaneState extends ConsumerState<EditRequestPane> {
.read(collectionStateNotifierProvider.notifier)
.getRequestModel(activeId!)
.requestTabIndex);
return Padding(
padding: const EdgeInsets.all(5),
child: TabContainer(
return TabContainer(
key: Key(activeId),
controller: _controller,
color: Colors.grey.shade100,
color: colorGrey100,
onEnd: () {
ref
.read(collectionStateNotifierProvider.notifier)
.update(activeId, requestTabIndex: _controller.index);
},
isStringTabs: false,
tabs: const [
Text(
'URL Params',
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
style: textStyleButton,
),
Text(
'Headers',
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
style: textStyleButton,
),
Text(
'Body',
textAlign: TextAlign.center,
overflow: TextOverflow.fade,
style: textStyleButton,
)
],
children: const [
EditRequestURLParams(),
EditRequestHeaders(),
EditRequestBody(),
],
),
);
}

View File

@ -1,5 +1,13 @@
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 '../../providers/providers.dart';
import '../../models/request_model.dart';
import '../styles.dart';
import "../../utils/utils.dart";
import "../../consts.dart";
class ResponsePane extends ConsumerStatefulWidget {
const ResponsePane({super.key});
@ -16,12 +24,28 @@ class _ResponsePaneState extends ConsumerState<ResponsePane> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(5),
child: Column(
children: [],
),
);
final activeId = ref.watch(activeItemIdStateProvider);
final collection = ref.read(collectionStateNotifierProvider);
final idIdx = collection.indexWhere((m) => m.id == activeId);
final status = ref.watch(collectionStateNotifierProvider
.select((value) => (value[idIdx].responseStatus,
value[idIdx].message,
value[idIdx].responseModel)));
if (status.$0 == null) {
return const NotSentWidget();
}
if (status.$0 == -1) {
return ErrorMessage(message: status.$1);
}
else{
//var responseModel = ref
// .read(collectionStateNotifierProvider.notifier)
// .getRequestModel(activeId!)
// .responseModel;
return ResponseViewer(statusCode: status.$0!,
message: status.$1,
responseModel: status.$2!);
}
}
@override
@ -29,3 +53,237 @@ class _ResponsePaneState extends ConsumerState<ResponsePane> {
super.dispose();
}
}
class NotSentWidget extends StatelessWidget {
const NotSentWidget({super.key});
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.north_east_rounded,
size: 40,
color: colorErrorMsg,
),
Text(
'Not Sent',
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(color: colorErrorMsg),
),
],
),
);
}
}
class ErrorMessage extends StatelessWidget {
const ErrorMessage({super.key, required this.message});
final String? message;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.warning_rounded,
size: 40,
color: colorErrorMsg,
),
Text(
message ?? 'And error occurred.',
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(color: colorErrorMsg),
),
],
),
);
}
}
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 StatelessWidget {
final int statusCode;
final String? message;
final ResponseModel responseModel;
const ResponseViewer({
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,
),
],
),
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),
],
),
),
);
}
}

View File

@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:davi/davi.dart';
import '../../models/models.dart';
import '../../providers/providers.dart';
import '../styles.dart';
class EditRequestURLParams extends ConsumerStatefulWidget {
const EditRequestURLParams({Key? key}) : super(key: key);
@ -26,7 +27,11 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
return TextFormField(
key: Key("$activeId-$idx-params-k"),
initialValue: rows[idx].k,
decoration: const InputDecoration(hintText: "Add URL Parameter"),
style: codeStyle,
decoration: InputDecoration(
hintStyle: codeStyle,
hintText: "Add URL Parameter",
),
onChanged: (value) {
rows[idx] = rows[idx].copyWith(k: value);
_onFieldChange(activeId!);
@ -39,7 +44,11 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
return TextFormField(
key: Key("$activeId-$idx-params-v"),
initialValue: rows[idx].v,
decoration: const InputDecoration(hintText: "Add Value"),
style: codeStyle,
decoration: InputDecoration(
hintStyle: codeStyle,
hintText: "Add Value",
),
onChanged: (value) {
rows[idx] = rows[idx].copyWith(v: value);
_onFieldChange(activeId!);
@ -75,10 +84,13 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
),
],
);
return Column(
return Container(
decoration: tableContainerDecoration,
margin: p5,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(10),
padding: p10,
child: Row(
children: [
ElevatedButton(
@ -86,33 +98,22 @@ class EditRequestURLParamsState extends ConsumerState<EditRequestURLParams> {
rows.add(const KVRow("", ""));
model.addRow(const KVRow("", ""));
},
child: const Text("+ Add Param"),
child: const Text(
"+ Add Param",
style: textStyleButton,
),
),
],
),
),
Expanded(
child: DaviTheme(
data: DaviThemeData(
columnDividerThickness: 1,
columnDividerColor: Colors.grey.shade100,
row: RowThemeData(dividerColor: Colors.grey.shade100),
decoration: const BoxDecoration(
border: Border(),
),
header: HeaderThemeData(
color: Colors.grey.shade100,
columnDividerColor: Colors.grey.shade100,
bottomBorderHeight: 1,
),
headerCell: const HeaderCellThemeData(
alignment: Alignment.center,
),
),
data: tableThemeData,
child: Davi<KVRow>(model),
),
),
],
),
);
}
}
@ -138,7 +139,11 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
return TextFormField(
key: Key("$activeId-$idx-headers-k"),
initialValue: rows[idx].k,
decoration: const InputDecoration(hintText: "Add Header Name"),
style: codeStyle,
decoration: InputDecoration(
hintStyle: codeStyle,
hintText: "Add Header Name",
),
onChanged: (value) {
rows[idx] = rows[idx].copyWith(k: value);
_onFieldChange(activeId!);
@ -151,7 +156,11 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
return TextFormField(
key: Key("$activeId-$idx-headers-v"),
initialValue: rows[idx].v,
decoration: const InputDecoration(hintText: "Add Header Value"),
style: codeStyle,
decoration: InputDecoration(
hintStyle: codeStyle,
hintText: "Add Header Value",
),
onChanged: (value) {
rows[idx] = rows[idx].copyWith(v: value);
_onFieldChange(activeId!);
@ -187,10 +196,13 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
),
],
);
return Column(
return Container(
decoration: tableContainerDecoration,
margin: p5,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(10),
padding: p10,
child: Row(
children: [
ElevatedButton(
@ -198,33 +210,22 @@ class EditRequestHeadersState extends ConsumerState<EditRequestHeaders> {
rows.add(const KVRow("", ""));
model.addRow(const KVRow("", ""));
},
child: const Text("+ Add Header"),
child: const Text(
"+ Add Header",
style: textStyleButton,
),
),
],
),
),
Expanded(
child: DaviTheme(
data: DaviThemeData(
columnDividerThickness: 1,
columnDividerColor: Colors.grey.shade100,
row: RowThemeData(dividerColor: Colors.grey.shade100),
decoration: const BoxDecoration(
border: Border(),
),
header: HeaderThemeData(
color: Colors.grey.shade100,
columnDividerColor: Colors.grey.shade100,
bottomBorderHeight: 1,
),
headerCell: const HeaderCellThemeData(
alignment: Alignment.center,
),
),
data: tableThemeData,
child: Davi<KVRow>(model),
),
),
],
),
);
}
}

View File

@ -26,23 +26,23 @@ class _EditorPaneRequestURLCardState
return Card(
shape: cardShape,
child: Padding(
padding: EdgeInsets.symmetric(
padding: const EdgeInsets.symmetric(
vertical: 5,
horizontal: 20,
),
child: Row(
children: [
DropdownButtonHTTPMethod(),
const DropdownButtonHTTPMethod(),
const SizedBox(
width: 20,
),
Expanded(
const Expanded(
child: URLTextField(),
),
const SizedBox(
width: 20,
),
Container(
SizedBox(
height: 36,
child: ElevatedButton(
onPressed: () async {
@ -51,8 +51,11 @@ class _EditorPaneRequestURLCardState
.sendRequest(activeId!);
},
child: Row(
children: [
Text("Send"),
children: const [
Text(
"Send",
style: textStyleButton,
),
SizedBox(
width: 10,
),
@ -80,14 +83,9 @@ class DropdownButtonHTTPMethod extends ConsumerStatefulWidget {
class _DropdownButtonHTTPMethodState
extends ConsumerState<DropdownButtonHTTPMethod> {
//late HTTPVerb dropdownValue;
@override
void initState() {
super.initState();
//dropdownValue = ref
// .read(collectionStateNotifierProvider.notifier)
// .idxOfId(String id);
}
@override
@ -97,34 +95,31 @@ class _DropdownButtonHTTPMethodState
final idIdx = collection.indexWhere((m) => m.id == activeId);
final method = ref.watch(
collectionStateNotifierProvider.select((value) => value[idIdx].method));
//final model = ref
// .read(collectionStateNotifierProvider.notifier)
// .getRequestModel(activeId!);
return DropdownButton<HTTPVerb>(
focusColor: Colors.white,
//value: collection[idIdx].method,
value: method, //model.method,
focusColor: colorBg,
value: method,
icon: const Icon(Icons.unfold_more_rounded),
elevation: 4,
underline: Container(
height: 0,
//color: Colors.deepPurpleAccent,
),
onChanged: (HTTPVerb? value) {
ref
.read(collectionStateNotifierProvider.notifier)
.update(activeId!, method: value);
},
borderRadius: BorderRadius.circular(10),
borderRadius: borderRadius10,
items: HTTPVerb.values.map<DropdownMenuItem<HTTPVerb>>((HTTPVerb value) {
return DropdownMenuItem<HTTPVerb>(
//alignment: AlignmentDirectional.center,
value: value,
child: Padding(
padding: const EdgeInsets.only(left: 16),
child: Text(
value.name.toUpperCase(),
style: TextStyle(color: getHTTPMethodColor(value)),
style: codeStyle.copyWith(
fontWeight: FontWeight.bold,
color: getHTTPMethodColor(value),
),
),
),
);
@ -157,9 +152,10 @@ class _URLTextFieldState extends ConsumerState<URLTextField> {
.read(collectionStateNotifierProvider.notifier)
.getRequestModel(activeId!)
.url,
style: codeStyle,
decoration: InputDecoration(
hintText: "Enter API endpoint like api.foss42.com/country/codes",
hintStyle: TextStyle(color: Colors.grey.shade500),
hintStyle: codeStyle.copyWith(color: colorGrey500),
border: InputBorder.none,
),
onChanged: (value) {

View File

@ -1,11 +1,51 @@
import 'package:flutter/material.dart';
import 'package:davi/davi.dart';
import 'package:google_fonts/google_fonts.dart';
final codeStyle = GoogleFonts.sourceCodePro();
const textStyleButton = TextStyle(fontWeight: FontWeight.bold);
const colorBg = Colors.white;
final colorGrey50 = Colors.grey.shade50;
final colorGrey100 = Colors.grey.shade100;
final colorGrey200 = Colors.grey.shade200;
final colorGrey300 = Colors.grey.shade300;
final colorGrey400 = Colors.grey.shade400;
final colorGrey500 = Colors.grey.shade500;
final colorErrorMsg = colorGrey500;
final borderRadius10 = BorderRadius.circular(10);
final cardShape = RoundedRectangleBorder(
side: const BorderSide(
color: Colors.white70,
width: 1,
),
borderRadius: BorderRadius.circular(10),
borderRadius: borderRadius10,
);
final colorErrorMsg = Colors.grey.shade500;
final tableContainerDecoration = BoxDecoration(
color: colorBg,
borderRadius: borderRadius10,
);
final tableThemeData = DaviThemeData(
columnDividerThickness: 1,
columnDividerColor: colorGrey100,
row: RowThemeData(dividerColor: colorGrey100),
decoration: const BoxDecoration(
border: Border(),
),
header: HeaderThemeData(
color: colorGrey50,
columnDividerColor: colorGrey100,
bottomBorderHeight: 1,
bottomBorderColor: colorGrey100,
),
headerCell: const HeaderCellThemeData(
alignment: Alignment.center,
textStyle: null,
),
);
const p5 = EdgeInsets.all(5);
const p10 = EdgeInsets.all(10);

View File

@ -4,3 +4,73 @@ enum ContentType { json, text }
const DEFAULT_METHOD = HTTPVerb.get;
const DEFAULT_BODY_CONTENT_TYPE = ContentType.json;
const JSON_MIMETYPE = 'application/json';
const RESPONSE_CODE_REASONS = {
// 100s
100: 'Continue',
101: 'Switching Protocols',
// 200s
200: 'OK',
201: 'Created',
202: 'Accepted',
203: 'Non-Authoritative Information',
204: 'No Content',
205: 'Reset Content',
206: 'Partial Content',
207: 'Multi-Status',
208: 'Already Reported',
226: 'IM Used',
// 300s
300: 'Multiple Choices',
301: 'Moved Permanently',
302: 'Found',
303: 'See Other',
304: 'Not Modified',
305: 'Use Proxy',
306: 'Switch Proxy',
307: 'Temporary Redirect',
308: 'Permanent Redirect',
// 400s
400: 'Bad Request',
401: 'Unauthorized',
402: 'Payment Required',
403: 'Forbidden',
404: 'Not Found',
405: 'Method Not Allowed',
406: 'Not Acceptable',
407: 'Proxy Authentication Required',
408: 'Request Timeout',
409: 'Conflict',
410: 'Gone',
411: 'Length Required',
412: 'Precondition Failed',
413: 'Payload Too Large',
414: 'URI Too Long',
415: 'Unsupported Media Type',
416: 'Range Not Satisfiable',
417: 'Expectation Failed',
418: "I'm a Teapot",
421: 'Misdirected Request',
422: 'Unprocessable Entity',
423: 'Locked',
424: 'Failed Dependency',
425: 'Too Early',
426: 'Upgrade Required',
428: 'Precondition Required',
429: 'Too Many Requests',
431: 'Request Header Fields Too Large',
451: 'Unavailable For Legal Reasons',
// 500s
500: 'Internal Server Error',
501: 'Not Implemented',
502: 'Bad Gateway',
503: 'Service Unavailable',
504: 'Gateway Timeout',
505: 'HTTP Version Not Supported',
506: 'Variant Also Negotiates',
507: 'Insufficient Storage',
508: 'Loop Detected',
510: 'Not Extended',
511: 'Network Authentication Required',
};

View File

@ -2,13 +2,14 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:desktop_window/desktop_window.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart';
import 'screens/screens.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
if (!kIsWeb) {
await DesktopWindow.setWindowSize(Size(1400, 800));
await DesktopWindow.setMinWindowSize(Size(1200, 800));
await DesktopWindow.setWindowSize(const Size(1400, 800));
await DesktopWindow.setMinWindowSize(const Size(1200, 800));
}
runApp(
const ProviderScope(
@ -25,7 +26,8 @@ class App extends StatelessWidget {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: GoogleFonts.openSans().fontFamily,
primarySwatch: Colors.indigo,
),
home: const HomePage(),
);

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:multi_split_view/multi_split_view.dart';
import '../components/components.dart';
import '../components/styles.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@ -32,8 +33,8 @@ class HomePageState extends State<HomePage> {
data: MultiSplitViewThemeData(
dividerThickness: 4,
dividerPainter: DividerPainters.background(
color: Colors.grey.shade200,
highlightedColor: Colors.grey.shade400,
color: colorGrey200,
highlightedColor: colorGrey400,
animationEnabled: false,
),
),

View File

@ -1,10 +1,10 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter_api_tool/consts.dart';
import 'package:http/http.dart' as http;
import 'package:collection/collection.dart' show mergeMaps;
import '../models/models.dart';
import '../../consts.dart';
const SUPPORTED_URI_SCHEMES = [
"https",

View File

@ -1,6 +1,25 @@
import 'package:flutter/material.dart';
import '../consts.dart';
Color? getResponseStatusCodeColor(int? statusCode) {
if (statusCode == null) {
return Colors.grey.shade700;
}
if (statusCode >= 500) {
return Colors.amber.shade900;
}
if (statusCode >= 400) {
return Colors.red.shade800;
}
if (statusCode >= 300) {
return Colors.blue.shade800;
}
if (statusCode >= 200) {
return Colors.green.shade800;
}
return Colors.grey.shade700;
}
Color getHTTPMethodColor(HTTPVerb method) {
Color col;
switch (method) {
@ -39,3 +58,22 @@ String getRequestTitleFromUrl(String? url) {
}
return url;
}
String humanizeDuration(Duration? duration) {
if (duration == null) {
return "";
}
if (duration.inMinutes >= 1) {
var min = duration.inMinutes;
var secs = duration.inSeconds.remainder(60);
return "$min.$secs m";
}
if (duration.inSeconds >= 1) {
var secs = duration.inSeconds;
var mili = duration.inMilliseconds.remainder(1000) ~/ 10;
return "$secs.$mili s";
} else {
var mili = duration.inMilliseconds.remainder(1000);
return "$mili ms";
}
}

View File

@ -42,7 +42,7 @@ packages:
source: hosted
version: "1.1.1"
collection:
dependency: transitive
dependency: "direct main"
description:
name: collection
sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0
@ -81,11 +81,35 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
ffi:
dependency: transitive
description:
name: ffi
sha256: a38574032c5f1dd06c4aee541789906c12ccaab8ba01446e800d9c5b79c4a978
url: "https://pub.dev"
source: hosted
version: "2.0.1"
file:
dependency: transitive
description:
name: file
sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_json_view:
dependency: "direct main"
description:
name: flutter_json_view
sha256: "4adc84e96c8bbd1ee874c5b8a3f6a404edccab9757a12edcde4c45c382b95214"
url: "https://pub.dev"
source: hosted
version: "1.0.0"
flutter_lints:
dependency: "direct dev"
description:
@ -107,6 +131,30 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
google_fonts:
dependency: "direct main"
description:
name: google_fonts
sha256: "927573f2e8a8d65c17931e21918ad0ab0666b1b636537de7c4932bdb487b190f"
url: "https://pub.dev"
source: hosted
version: "4.0.3"
http:
dependency: "direct main"
description:
name: http
sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482"
url: "https://pub.dev"
source: hosted
version: "0.13.5"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
js:
dependency: transitive
description:
@ -163,6 +211,78 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.8.2"
path_provider:
dependency: transitive
description:
name: path_provider
sha256: "04890b994ee89bfa80bf3080bfec40d5a92c5c7a785ebb02c13084a099d2b6f9"
url: "https://pub.dev"
source: hosted
version: "2.0.13"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: "7623b7d4be0f0f7d9a8b5ee6879fc13e4522d4c875ab86801dee4af32b54b83e"
url: "https://pub.dev"
source: hosted
version: "2.0.23"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: eec003594f19fe2456ea965ae36b3fc967bc5005f508890aafe31fa75e41d972
url: "https://pub.dev"
source: hosted
version: "2.1.2"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: "525ad5e07622d19447ad740b1ed5070031f7a5437f44355ae915ff56e986429a"
url: "https://pub.dev"
source: hosted
version: "2.1.9"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
url: "https://pub.dev"
source: hosted
version: "2.0.6"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: "642ddf65fde5404f83267e8459ddb4556316d3ee6d511ed193357e25caa3632d"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
platform:
dependency: transitive
description:
name: platform
sha256: "4a451831508d7d6ca779f7ac6e212b4023dd5a7d08a27a63da33756410e32b76"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
process:
dependency: transitive
description:
name: process
sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09"
url: "https://pub.dev"
source: hosted
version: "4.2.4"
riverpod:
dependency: transitive
description:
@ -264,6 +384,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
win32:
dependency: transitive
description:
name: win32
sha256: c9ebe7ee4ab0c2194e65d3a07d8c54c5d00bb001b76081c4a04cdb8448b59e46
url: "https://pub.dev"
source: hosted
version: "3.1.3"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: ee1505df1426458f7f60aac270645098d318a8b4766d85fde75f76f2e21807d1
url: "https://pub.dev"
source: hosted
version: "1.0.0"
sdks:
dart: ">=2.19.2 <3.0.0"
flutter: ">=3.0.0"

View File

@ -17,6 +17,8 @@ dependencies:
davi: ^3.2.0
http: ^0.13.5
collection: ^1.17.0
flutter_json_view: ^1.0.0
google_fonts: ^4.0.3
dev_dependencies:
flutter_test: