mirror of
https://github.com/foss42/apidash.git
synced 2025-06-05 01:46:21 +08:00
Fully functional prototype with final UI design
This commit is contained in:
@ -1 +1,11 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
analyzer:
|
||||
errors:
|
||||
invalid_annotation_target: ignore
|
||||
enable-experiment:
|
||||
- records
|
||||
|
||||
linter:
|
||||
rules:
|
||||
constant_identifier_names: false
|
||||
|
@ -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)
|
||||
|
@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
|
@ -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(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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',
|
||||
};
|
||||
|
@ -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(),
|
||||
);
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
|
@ -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",
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
138
pubspec.lock
138
pubspec.lock
@ -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"
|
||||
|
@ -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:
|
||||
|
Reference in New Issue
Block a user