fix: review changes

This commit is contained in:
DenserMeerkat
2024-06-24 23:41:52 +05:30
parent 7852fe98e5
commit 2f8a1ef9b2
74 changed files with 1929 additions and 1811 deletions

View File

@ -0,0 +1,23 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
class ClearResponseButton extends StatelessWidget {
const ClearResponseButton({
super.key,
this.onPressed,
});
final VoidCallback? onPressed;
@override
Widget build(BuildContext context) {
return IconButton(
tooltip: kTooltipClearResponse,
onPressed: onPressed,
icon: const Icon(
Icons.delete,
size: 16,
),
);
}
}

View File

@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:apidash/consts.dart';
import "snackbars.dart";
class CopyButton extends StatelessWidget {
const CopyButton({
super.key,
required this.toCopy,
this.showLabel = true,
});
final String toCopy;
final bool showLabel;
@override
Widget build(BuildContext context) {
var sm = ScaffoldMessenger.of(context);
return Tooltip(
message: showLabel ? '' : kLabelCopy,
child: SizedBox(
width: showLabel ? null : kTextButtonMinWidth,
child: TextButton(
onPressed: () async {
await Clipboard.setData(ClipboardData(text: toCopy));
sm.hideCurrentSnackBar();
sm.showSnackBar(getSnackBar("Copied"));
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.content_copy,
size: 20,
),
if (showLabel) const Text(kLabelCopy)
],
),
),
),
);
}
}

View File

@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:apidash/consts.dart';
class DiscordButton extends StatelessWidget {
const DiscordButton({
super.key,
this.text,
});
final String? text;
@override
Widget build(BuildContext context) {
var label = text ?? 'Discord Server';
return FilledButton.icon(
onPressed: () {
launchUrl(Uri.parse(kDiscordUrl));
},
icon: const Icon(
Icons.discord,
size: 20.0,
),
label: Text(
label,
style: kTextStyleButton,
),
);
}
}

View File

@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:apidash/consts.dart';
class RepoButton extends StatelessWidget {
const RepoButton({
super.key,
this.text,
this.icon,
});
final String? text;
final IconData? icon;
@override
Widget build(BuildContext context) {
var label = text ?? "GitHub";
if (icon == null) {
return FilledButton(
onPressed: () {
launchUrl(Uri.parse(kGitUrl));
},
child: Text(
label,
style: kTextStyleButton,
),
);
}
return FilledButton.icon(
onPressed: () {
launchUrl(Uri.parse(kGitUrl));
},
icon: Icon(
icon,
size: 20.0,
),
label: Text(
label,
style: kTextStyleButton,
),
);
}
}

View File

@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:apidash/utils/utils.dart';
import 'package:apidash/consts.dart';
import "snackbars.dart";
class SaveInDownloadsButton extends StatelessWidget {
const SaveInDownloadsButton({
super.key,
this.content,
this.mimeType,
this.ext,
this.name,
this.showLabel = true,
});
final Uint8List? content;
final String? mimeType;
final String? ext;
final String? name;
final bool showLabel;
@override
Widget build(BuildContext context) {
var sm = ScaffoldMessenger.of(context);
return Tooltip(
message: showLabel ? '' : kLabelDownload,
child: SizedBox(
width: showLabel ? null : kTextButtonMinWidth,
child: TextButton(
onPressed: (content != null)
? () async {
var message = "";
var path = await getFileDownloadpath(
name,
ext ?? getFileExtension(mimeType),
);
if (path != null) {
try {
await saveFile(path, content!);
var sp = getShortPath(path);
message = 'Saved to $sp';
} catch (e) {
message = "An error occurred while saving file.";
}
} else {
message = "Unable to determine the download path.";
}
sm.hideCurrentSnackBar();
sm.showSnackBar(getSnackBar(message, small: false));
}
: null,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.download,
size: 20,
),
if (showLabel) const Text(kLabelDownload)
],
),
),
),
);
}
}

View File

@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
class SendButton extends StatelessWidget {
const SendButton({
super.key,
required this.isWorking,
required this.onTap,
});
final bool isWorking;
final void Function() onTap;
@override
Widget build(BuildContext context) {
return FilledButton(
onPressed: isWorking ? null : onTap,
child: Row(
mainAxisSize: MainAxisSize.min,
children: isWorking
? const [
Text(
kLabelSending,
style: kTextStyleButton,
),
]
: const [
Text(
kLabelSend,
style: kTextStyleButton,
),
kHSpacer10,
Icon(
size: 16,
Icons.send,
),
],
),
);
}
}

View File

@ -1,234 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:apidash/utils/utils.dart';
import 'package:apidash/consts.dart';
import "snackbars.dart";
class CopyButton extends StatelessWidget {
const CopyButton({
super.key,
required this.toCopy,
this.showLabel = true,
});
final String toCopy;
final bool showLabel;
@override
Widget build(BuildContext context) {
var sm = ScaffoldMessenger.of(context);
return Tooltip(
message: showLabel ? '' : kLabelCopy,
child: SizedBox(
width: showLabel ? null : kTextButtonMinWidth,
child: TextButton(
onPressed: () async {
await Clipboard.setData(ClipboardData(text: toCopy));
sm.hideCurrentSnackBar();
sm.showSnackBar(getSnackBar("Copied"));
},
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.content_copy,
size: 20,
),
if (showLabel) const Text(kLabelCopy)
],
),
),
),
);
}
}
class SendRequestButton extends StatelessWidget {
const SendRequestButton({
super.key,
required this.isWorking,
required this.onTap,
});
final bool isWorking;
final void Function() onTap;
@override
Widget build(BuildContext context) {
return FilledButton(
onPressed: isWorking ? null : onTap,
child: Row(
mainAxisSize: MainAxisSize.min,
children: isWorking
? const [
Text(
kLabelSending,
style: kTextStyleButton,
),
]
: const [
Text(
kLabelSend,
style: kTextStyleButton,
),
kHSpacer10,
Icon(
size: 16,
Icons.send,
),
],
),
);
}
}
class SaveInDownloadsButton extends StatelessWidget {
const SaveInDownloadsButton({
super.key,
this.content,
this.mimeType,
this.ext,
this.name,
this.showLabel = true,
});
final Uint8List? content;
final String? mimeType;
final String? ext;
final String? name;
final bool showLabel;
@override
Widget build(BuildContext context) {
var sm = ScaffoldMessenger.of(context);
return Tooltip(
message: showLabel ? '' : kLabelDownload,
child: SizedBox(
width: showLabel ? null : kTextButtonMinWidth,
child: TextButton(
onPressed: (content != null)
? () async {
var message = "";
var path = await getFileDownloadpath(
name,
ext ?? getFileExtension(mimeType),
);
if (path != null) {
try {
await saveFile(path, content!);
var sp = getShortPath(path);
message = 'Saved to $sp';
} catch (e) {
message = "An error occurred while saving file.";
}
} else {
message = "Unable to determine the download path.";
}
sm.hideCurrentSnackBar();
sm.showSnackBar(getSnackBar(message, small: false));
}
: null,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(
Icons.download,
size: 20,
),
if (showLabel) const Text(kLabelDownload)
],
),
),
),
);
}
}
class RepoButton extends StatelessWidget {
const RepoButton({
super.key,
this.text,
this.icon,
});
final String? text;
final IconData? icon;
@override
Widget build(BuildContext context) {
var label = text ?? "GitHub";
if (icon == null) {
return FilledButton(
onPressed: () {
launchUrl(Uri.parse(kGitUrl));
},
child: Text(
label,
style: kTextStyleButton,
),
);
}
return FilledButton.icon(
onPressed: () {
launchUrl(Uri.parse(kGitUrl));
},
icon: Icon(
icon,
size: 20.0,
),
label: Text(
label,
style: kTextStyleButton,
),
);
}
}
class DiscordButton extends StatelessWidget {
const DiscordButton({
super.key,
this.text,
});
final String? text;
@override
Widget build(BuildContext context) {
var label = text ?? 'Discord Server';
return FilledButton.icon(
onPressed: () {
launchUrl(Uri.parse(kDiscordUrl));
},
icon: const Icon(
Icons.discord,
size: 20.0,
),
label: Text(
label,
style: kTextStyleButton,
),
);
}
}
class ClearResponseButton extends StatelessWidget {
const ClearResponseButton({
super.key,
this.onPressed,
});
final VoidCallback? onPressed;
@override
Widget build(BuildContext context) {
return IconButton(
tooltip: kTooltipClearResponse,
onPressed: onPressed,
icon: const Icon(
Icons.delete,
size: 16,
),
);
}
}

View File

@ -0,0 +1,22 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
class RequestDetailsCard extends StatelessWidget {
const RequestDetailsCard({super.key, this.child});
final Widget? child;
@override
@override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(
side: BorderSide(
color: Theme.of(context).colorScheme.surfaceVariant,
),
borderRadius: kBorderRadius12,
),
elevation: 0,
child: child,
);
}
}

View File

@ -0,0 +1,127 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
import 'package:apidash/utils/utils.dart';
import 'menus.dart' show ItemCardMenu;
class SidebarEnvironmentCard extends StatelessWidget {
const SidebarEnvironmentCard({
super.key,
required this.id,
this.isGlobal = false,
this.isActive = false,
this.name,
this.selectedId,
this.editRequestId,
this.setActive,
this.onTap,
this.onDoubleTap,
this.onSecondaryTap,
this.onChangedNameEditor,
this.focusNode,
this.onTapOutsideNameEditor,
this.onMenuSelected,
});
final String id;
final bool isGlobal;
final bool isActive;
final String? name;
final String? selectedId;
final String? editRequestId;
final void Function(bool?)? setActive;
final void Function()? onTap;
final void Function()? onDoubleTap;
final void Function()? onSecondaryTap;
final Function(String)? onChangedNameEditor;
final FocusNode? focusNode;
final Function()? onTapOutsideNameEditor;
final Function(ItemMenuOption)? onMenuSelected;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final Color color =
isGlobal ? colorScheme.secondaryContainer : colorScheme.surface;
final Color colorVariant = colorScheme.surfaceVariant.withOpacity(0.5);
final Color surfaceTint = colorScheme.primary;
bool isSelected = selectedId == id;
bool inEditMode = editRequestId == id;
String nm = getEnvironmentTitle(name);
return Tooltip(
message: nm,
triggerMode: TooltipTriggerMode.manual,
waitDuration: const Duration(seconds: 1),
child: Card(
shape: const RoundedRectangleBorder(
borderRadius: kBorderRadius8,
),
elevation: isSelected ? 1 : 0,
surfaceTintColor: isSelected ? surfaceTint : null,
color: isSelected && !isGlobal
? colorScheme.brightness == Brightness.dark
? colorVariant
: color
: color,
margin: EdgeInsets.zero,
child: InkWell(
borderRadius: kBorderRadius8,
hoverColor: colorVariant,
focusColor: colorVariant.withOpacity(0.5),
onTap: inEditMode ? null : onTap,
onSecondaryTap: onSecondaryTap,
child: Padding(
padding: EdgeInsets.only(
left: 6,
right: isSelected ? 6 : 10,
top: 5,
bottom: 5,
),
child: SizedBox(
height: 20,
child: Row(
children: [
kHSpacer4,
Expanded(
child: inEditMode
? TextFormField(
key: ValueKey("$id-name"),
initialValue: name,
focusNode: focusNode,
style: Theme.of(context).textTheme.bodyMedium,
onTapOutside: (_) {
onTapOutsideNameEditor?.call();
},
onFieldSubmitted: (value) {
onTapOutsideNameEditor?.call();
},
onChanged: onChangedNameEditor,
decoration: const InputDecoration(
isCollapsed: true,
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
),
)
: Text(
nm,
softWrap: false,
overflow: TextOverflow.fade,
),
),
Visibility(
visible: isSelected && !inEditMode && !isGlobal,
child: SizedBox(
width: 28,
child: ItemCardMenu(
onSelected: onMenuSelected,
),
),
),
],
),
),
),
),
),
);
}
}

View File

@ -132,146 +132,3 @@ class SidebarRequestCard extends StatelessWidget {
);
}
}
class RequestDetailsCard extends StatelessWidget {
const RequestDetailsCard({super.key, this.child});
final Widget? child;
@override
@override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(
side: BorderSide(
color: Theme.of(context).colorScheme.surfaceVariant,
),
borderRadius: kBorderRadius12,
),
elevation: 0,
child: child,
);
}
}
class SidebarEnvironmentCard extends StatelessWidget {
const SidebarEnvironmentCard({
super.key,
required this.id,
this.isGlobal = false,
this.isActive = false,
this.name,
this.selectedId,
this.editRequestId,
this.setActive,
this.onTap,
this.onDoubleTap,
this.onSecondaryTap,
this.onChangedNameEditor,
this.focusNode,
this.onTapOutsideNameEditor,
this.onMenuSelected,
});
final String id;
final bool isGlobal;
final bool isActive;
final String? name;
final String? selectedId;
final String? editRequestId;
final void Function(bool?)? setActive;
final void Function()? onTap;
final void Function()? onDoubleTap;
final void Function()? onSecondaryTap;
final Function(String)? onChangedNameEditor;
final FocusNode? focusNode;
final Function()? onTapOutsideNameEditor;
final Function(ItemMenuOption)? onMenuSelected;
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final Color color =
isGlobal ? colorScheme.secondaryContainer : colorScheme.surface;
final Color colorVariant = colorScheme.surfaceVariant.withOpacity(0.5);
final Color surfaceTint = colorScheme.primary;
bool isSelected = selectedId == id;
bool inEditMode = editRequestId == id;
String nm = getEnvironmentTitle(name);
return Tooltip(
message: nm,
triggerMode: TooltipTriggerMode.manual,
waitDuration: const Duration(seconds: 1),
child: Card(
shape: const RoundedRectangleBorder(
borderRadius: kBorderRadius8,
),
elevation: isSelected ? 1 : 0,
surfaceTintColor: isSelected ? surfaceTint : null,
color: isSelected && !isGlobal
? colorScheme.brightness == Brightness.dark
? colorVariant
: color
: color,
margin: EdgeInsets.zero,
child: InkWell(
borderRadius: kBorderRadius8,
hoverColor: colorVariant,
focusColor: colorVariant.withOpacity(0.5),
onTap: inEditMode ? null : onTap,
onSecondaryTap: onSecondaryTap,
child: Padding(
padding: EdgeInsets.only(
left: 6,
right: isSelected ? 6 : 10,
top: 5,
bottom: 5,
),
child: SizedBox(
height: 20,
child: Row(
children: [
kHSpacer4,
Expanded(
child: inEditMode
? TextFormField(
key: ValueKey("$id-name"),
initialValue: name,
focusNode: focusNode,
style: Theme.of(context).textTheme.bodyMedium,
onTapOutside: (_) {
onTapOutsideNameEditor?.call();
},
onFieldSubmitted: (value) {
onTapOutsideNameEditor?.call();
},
onChanged: onChangedNameEditor,
decoration: const InputDecoration(
isCollapsed: true,
contentPadding: EdgeInsets.zero,
border: InputBorder.none,
),
)
: Text(
nm,
softWrap: false,
overflow: TextOverflow.fade,
),
),
Visibility(
visible: isSelected && !inEditMode && !isGlobal,
child: SizedBox(
width: 28,
child: ItemCardMenu(
onSelected: onMenuSelected,
),
),
),
],
),
),
),
),
),
);
}
}

41
lib/widgets/dialogs.dart Normal file
View File

@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
showRenameDialog(
BuildContext context,
String dialogTitle,
String? name,
Function(String) onRename,
) {
showDialog(
context: context,
builder: (context) {
final controller = TextEditingController(text: name ?? "");
controller.selection =
TextSelection(baseOffset: 0, extentOffset: controller.text.length);
return AlertDialog(
title: Text(dialogTitle),
content: TextField(
autofocus: true,
controller: controller,
decoration: const InputDecoration(hintText: "Enter new name"),
),
actions: <Widget>[
OutlinedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('CANCEL')),
FilledButton(
onPressed: () {
final val = controller.text.trim();
onRename(val);
Navigator.pop(context);
Future.delayed(const Duration(milliseconds: 100), () {
controller.dispose();
});
},
child: const Text('OK')),
],
);
});
}

View File

@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
class DropdownButtonCodegenLanguage extends StatelessWidget {
const DropdownButtonCodegenLanguage({
super.key,
this.codegenLanguage,
this.onChanged,
});
final CodegenLanguage? codegenLanguage;
final void Function(CodegenLanguage?)? onChanged;
@override
Widget build(BuildContext context) {
final surfaceColor = Theme.of(context).colorScheme.surface;
return DropdownButton<CodegenLanguage>(
isExpanded: true,
focusColor: surfaceColor,
value: codegenLanguage,
icon: const Icon(
Icons.unfold_more_rounded,
size: 16,
),
elevation: 4,
style: kCodeStyle.copyWith(
color: Theme.of(context).colorScheme.primary,
),
underline: Container(
height: 0,
),
onChanged: onChanged,
borderRadius: kBorderRadius12,
items: CodegenLanguage.values
.map<DropdownMenuItem<CodegenLanguage>>((CodegenLanguage value) {
return DropdownMenuItem<CodegenLanguage>(
value: value,
child: Padding(
padding: kPs8,
child: Text(
value.label,
style: kTextStyleButton,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
);
}).toList(),
);
}
}

View File

@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
class DropdownButtonContentType extends StatelessWidget {
const DropdownButtonContentType({
super.key,
this.contentType,
this.onChanged,
});
final ContentType? contentType;
final void Function(ContentType?)? onChanged;
@override
Widget build(BuildContext context) {
final surfaceColor = Theme.of(context).colorScheme.surface;
return DropdownButton<ContentType>(
focusColor: surfaceColor,
value: contentType,
icon: const Icon(
Icons.unfold_more_rounded,
size: 16,
),
elevation: 4,
style: kCodeStyle.copyWith(
color: Theme.of(context).colorScheme.primary,
),
underline: Container(
height: 0,
),
onChanged: onChanged,
borderRadius: kBorderRadius12,
items: ContentType.values
.map<DropdownMenuItem<ContentType>>((ContentType value) {
return DropdownMenuItem<ContentType>(
value: value,
child: Padding(
padding: kPs8,
child: Text(
value.name,
style: kTextStyleButton,
),
),
);
}).toList(),
);
}
}

View File

@ -0,0 +1,47 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
class DropdownButtonFormData extends StatelessWidget {
const DropdownButtonFormData({
super.key,
this.formDataType,
this.onChanged,
});
final FormDataType? formDataType;
final void Function(FormDataType?)? onChanged;
@override
Widget build(BuildContext context) {
final surfaceColor = Theme.of(context).colorScheme.surface;
return DropdownButton<FormDataType>(
dropdownColor: surfaceColor,
focusColor: surfaceColor,
value: formDataType,
icon: const Icon(
Icons.unfold_more_rounded,
size: 16,
),
elevation: 4,
style: kCodeStyle.copyWith(
color: Theme.of(context).colorScheme.primary,
),
underline: const IgnorePointer(),
onChanged: onChanged,
borderRadius: kBorderRadius12,
items: FormDataType.values
.map<DropdownMenuItem<FormDataType>>((FormDataType value) {
return DropdownMenuItem<FormDataType>(
value: value,
child: Padding(
padding: kPs8,
child: Text(
value.name,
style: kTextStyleButton,
),
),
);
}).toList(),
);
}
}

View File

@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:apidash/utils/utils.dart';
import 'package:apidash/consts.dart';
import 'package:apidash/extensions/extensions.dart';
class DropdownButtonHttpMethod extends StatelessWidget {
const DropdownButtonHttpMethod({
super.key,
this.method,
this.onChanged,
});
final HTTPVerb? method;
final void Function(HTTPVerb? value)? onChanged;
@override
Widget build(BuildContext context) {
final surfaceColor = Theme.of(context).colorScheme.surface;
return DropdownButton<HTTPVerb>(
focusColor: surfaceColor,
value: method,
icon: const Icon(Icons.unfold_more_rounded),
elevation: 4,
underline: Container(
height: 0,
),
borderRadius: kBorderRadius12,
onChanged: onChanged,
items: HTTPVerb.values.map<DropdownMenuItem<HTTPVerb>>((HTTPVerb value) {
return DropdownMenuItem<HTTPVerb>(
value: value,
child: Padding(
padding: EdgeInsets.only(left: context.isMediumWindow ? 8 : 16),
child: Text(
value.name.toUpperCase(),
style: kCodeStyle.copyWith(
fontWeight: FontWeight.bold,
color: getHTTPMethodColor(
value,
brightness: Theme.of(context).brightness,
),
),
),
),
);
}).toList(),
);
}
}

View File

@ -1,189 +0,0 @@
import 'package:flutter/material.dart';
import 'package:apidash/utils/utils.dart';
import 'package:apidash/consts.dart';
import 'package:apidash/extensions/extensions.dart';
class DropdownButtonHttpMethod extends StatelessWidget {
const DropdownButtonHttpMethod({
super.key,
this.method,
this.onChanged,
});
final HTTPVerb? method;
final void Function(HTTPVerb? value)? onChanged;
@override
Widget build(BuildContext context) {
final surfaceColor = Theme.of(context).colorScheme.surface;
return DropdownButton<HTTPVerb>(
focusColor: surfaceColor,
value: method,
icon: const Icon(Icons.unfold_more_rounded),
elevation: 4,
underline: Container(
height: 0,
),
borderRadius: kBorderRadius12,
onChanged: onChanged,
items: HTTPVerb.values.map<DropdownMenuItem<HTTPVerb>>((HTTPVerb value) {
return DropdownMenuItem<HTTPVerb>(
value: value,
child: Padding(
padding: EdgeInsets.only(left: context.isMediumWindow ? 8 : 16),
child: Text(
value.name.toUpperCase(),
style: kCodeStyle.copyWith(
fontWeight: FontWeight.bold,
color: getHTTPMethodColor(
value,
brightness: Theme.of(context).brightness,
),
),
),
),
);
}).toList(),
);
}
}
class DropdownButtonContentType extends StatelessWidget {
const DropdownButtonContentType({
super.key,
this.contentType,
this.onChanged,
});
final ContentType? contentType;
final void Function(ContentType?)? onChanged;
@override
Widget build(BuildContext context) {
final surfaceColor = Theme.of(context).colorScheme.surface;
return DropdownButton<ContentType>(
focusColor: surfaceColor,
value: contentType,
icon: const Icon(
Icons.unfold_more_rounded,
size: 16,
),
elevation: 4,
style: kCodeStyle.copyWith(
color: Theme.of(context).colorScheme.primary,
),
underline: Container(
height: 0,
),
onChanged: onChanged,
borderRadius: kBorderRadius12,
items: ContentType.values
.map<DropdownMenuItem<ContentType>>((ContentType value) {
return DropdownMenuItem<ContentType>(
value: value,
child: Padding(
padding: kPs8,
child: Text(
value.name,
style: kTextStyleButton,
),
),
);
}).toList(),
);
}
}
class DropdownButtonFormData extends StatelessWidget {
const DropdownButtonFormData({
super.key,
this.formDataType,
this.onChanged,
});
final FormDataType? formDataType;
final void Function(FormDataType?)? onChanged;
@override
Widget build(BuildContext context) {
final surfaceColor = Theme.of(context).colorScheme.surface;
return DropdownButton<FormDataType>(
dropdownColor: surfaceColor,
focusColor: surfaceColor,
value: formDataType,
icon: const Icon(
Icons.unfold_more_rounded,
size: 16,
),
elevation: 4,
style: kCodeStyle.copyWith(
color: Theme.of(context).colorScheme.primary,
),
underline: const IgnorePointer(),
onChanged: onChanged,
borderRadius: kBorderRadius12,
items: FormDataType.values
.map<DropdownMenuItem<FormDataType>>((FormDataType value) {
return DropdownMenuItem<FormDataType>(
value: value,
child: Padding(
padding: kPs8,
child: Text(
value.name,
style: kTextStyleButton,
),
),
);
}).toList(),
);
}
}
class DropdownButtonCodegenLanguage extends StatelessWidget {
const DropdownButtonCodegenLanguage({
super.key,
this.codegenLanguage,
this.onChanged,
});
final CodegenLanguage? codegenLanguage;
final void Function(CodegenLanguage?)? onChanged;
@override
Widget build(BuildContext context) {
final surfaceColor = Theme.of(context).colorScheme.surface;
return DropdownButton<CodegenLanguage>(
isExpanded: true,
focusColor: surfaceColor,
value: codegenLanguage,
icon: const Icon(
Icons.unfold_more_rounded,
size: 16,
),
elevation: 4,
style: kCodeStyle.copyWith(
color: Theme.of(context).colorScheme.primary,
),
underline: Container(
height: 0,
),
onChanged: onChanged,
borderRadius: kBorderRadius12,
items: CodegenLanguage.values
.map<DropdownMenuItem<CodegenLanguage>>((CodegenLanguage value) {
return DropdownMenuItem<CodegenLanguage>(
value: value,
child: Padding(
padding: kPs8,
child: Text(
value.label,
style: kTextStyleButton,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
),
);
}).toList(),
);
}
}

View File

@ -1,261 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_portal/flutter_portal.dart';
import 'package:mention_tag_text_field/mention_tag_text_field.dart';
import 'package:apidash/models/models.dart';
import 'package:apidash/consts.dart';
import '../screens/common/environment_autocomplete.dart';
class EnvironmentAutocompleteFieldBase extends StatefulHookWidget {
const EnvironmentAutocompleteFieldBase({
super.key,
this.initialValue,
this.onChanged,
this.onFieldSubmitted,
this.style,
this.decoration,
this.initialMentions,
this.suggestions,
this.mentionValue,
required this.onMentionValueChanged,
});
final String? initialValue;
final void Function(String)? onChanged;
final void Function(String)? onFieldSubmitted;
final TextStyle? style;
final InputDecoration? decoration;
final List<(String, Object?, Widget?)>? initialMentions;
final List<EnvironmentVariableSuggestion>? suggestions;
final String? mentionValue;
final void Function(String?) onMentionValueChanged;
@override
State<EnvironmentAutocompleteFieldBase> createState() =>
_EnvironmentAutocompleteFieldBaseState();
}
class _EnvironmentAutocompleteFieldBaseState
extends State<EnvironmentAutocompleteFieldBase> {
final MentionTagTextEditingController controller =
MentionTagTextEditingController();
final FocusNode focusNode = FocusNode();
@override
void initState() {
super.initState();
controller.text = widget.initialValue ?? "";
}
@override
Widget build(BuildContext context) {
final isSuggestionsVisible = useState(false);
return PortalTarget(
visible: isSuggestionsVisible.value && focusNode.hasFocus,
portalFollower: EnvironmentSuggestionsMenu(
mentionController: controller,
suggestions: widget.suggestions,
onSelect: (suggestion) {
controller.addMention(
label: '{${suggestion.variable.key}}}',
data: suggestion,
stylingWidget: EnvironmentVariableSpan(suggestion: suggestion));
widget.onChanged?.call(controller.text);
widget.onMentionValueChanged.call(null);
isSuggestionsVisible.value = false;
var mentionsCharacters =
controller.mentions.fold<int>(0, (previousValue, element) {
return previousValue + element.variable.key.length + 4 as int;
});
controller.selection = TextSelection.collapsed(
offset: controller.text.length +
controller.mentions.length -
mentionsCharacters,
);
},
),
anchor: const Aligned(
follower: Alignment.topLeft,
target: Alignment.bottomLeft,
backup: Aligned(
follower: Alignment.bottomLeft,
target: Alignment.topLeft,
),
),
child: EnvironmentMentionField(
focusNode: focusNode,
controller: controller,
initialMentions: widget.initialMentions ?? [],
mentionValue: widget.mentionValue,
onMentionValueChanged: widget.onMentionValueChanged,
isSuggestionsVisible: isSuggestionsVisible,
onChanged: widget.onChanged,
onFieldSubmitted: widget.onFieldSubmitted,
style: widget.style,
decoration: widget.decoration,
),
);
}
}
class EnvironmentMentionField extends StatelessWidget {
const EnvironmentMentionField({
super.key,
required this.focusNode,
required this.controller,
required this.initialMentions,
required this.mentionValue,
required this.onMentionValueChanged,
required this.isSuggestionsVisible,
this.onChanged,
this.onFieldSubmitted,
this.style,
this.decoration,
});
final FocusNode focusNode;
final MentionTagTextEditingController controller;
final List<(String, Object?, Widget?)> initialMentions;
final String? mentionValue;
final void Function(String?) onMentionValueChanged;
final ValueNotifier<bool> isSuggestionsVisible;
final void Function(String)? onChanged;
final void Function(String)? onFieldSubmitted;
final TextStyle? style;
final InputDecoration? decoration;
void onMention(String? value) {
onMentionValueChanged.call(value);
if (value != null) {
isSuggestionsVisible.value = true;
} else {
isSuggestionsVisible.value = false;
}
}
@override
Widget build(BuildContext context) {
return MentionTagTextFormField(
focusNode: focusNode,
onTap: () {
focusNode.requestFocus();
},
onTapOutside: (event) {
focusNode.unfocus();
isSuggestionsVisible.value = false;
},
controller: controller,
style: style,
initialMentions: initialMentions,
onMention: onMention,
onChanged: (value) {
onChanged?.call(controller.text);
},
onFieldSubmitted: (value) {
onFieldSubmitted?.call(controller.text);
isSuggestionsVisible.value = false;
},
decoration: decoration,
mentionTagDecoration: const MentionTagDecoration(
mentionStart: ['{'],
mentionBreak:
" ", // This is a workaround for the exception but adds a space after the mention
maxWords: 1,
allowDecrement: false,
allowEmbedding: true,
showMentionStartSymbol: false,
),
);
}
}
class EnvironmentSuggestionsMenu extends StatelessWidget {
const EnvironmentSuggestionsMenu({
super.key,
required this.mentionController,
required this.suggestions,
this.onSelect,
});
final MentionTagTextEditingController mentionController;
final List<EnvironmentVariableSuggestion>? suggestions;
final Function(EnvironmentVariableSuggestion)? onSelect;
@override
Widget build(BuildContext context) {
return suggestions == null || suggestions!.isEmpty
? const SizedBox.shrink()
: ClipRRect(
borderRadius: kBorderRadius8,
child: Material(
type: MaterialType.card,
elevation: 8,
child: ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 200),
child: Ink(
width: 300,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: kBorderRadius8,
border: Border.all(
color: Theme.of(context).colorScheme.outlineVariant,
),
),
child: ListView.separated(
shrinkWrap: true,
itemCount: suggestions?.length ?? 0,
separatorBuilder: (context, index) => const Divider(
height: 2,
),
itemBuilder: (context, index) {
final suggestion = suggestions![index];
return ListTile(
dense: true,
leading: EnvironmentIndicator(suggestion: suggestion),
title: Text(suggestion.variable.key),
subtitle: Text(suggestion.variable.value),
onTap: () {
onSelect?.call(suggestions![index]);
},
);
},
),
),
),
),
);
}
}
class EnvironmentIndicator extends StatelessWidget {
const EnvironmentIndicator({super.key, required this.suggestion});
final EnvironmentVariableSuggestion suggestion;
@override
Widget build(BuildContext context) {
final isUnknown = suggestion.isUnknown;
final isGlobal = suggestion.environmentId == kGlobalEnvironmentId;
return Container(
padding: kP4,
decoration: BoxDecoration(
color: isUnknown
? Theme.of(context).colorScheme.errorContainer
: isGlobal
? Theme.of(context).colorScheme.secondaryContainer
: Theme.of(context).colorScheme.primaryContainer,
borderRadius: kBorderRadius4,
),
child: Icon(
isUnknown
? Icons.block
: isGlobal
? Icons.public
: Icons.computer,
size: 16,
),
);
}
}

View File

@ -0,0 +1,104 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_portal/flutter_portal.dart';
import 'package:mention_tag_text_field/mention_tag_text_field.dart';
import 'package:apidash/models/models.dart';
import 'package:apidash/widgets/field_mention.dart';
import '../screens/common_widgets/common_widgets.dart';
import 'suggestions_menu.dart';
class EnvironmentFieldBase extends StatefulHookWidget {
const EnvironmentFieldBase({
super.key,
this.initialValue,
this.onChanged,
this.onFieldSubmitted,
this.style,
this.decoration,
this.initialMentions,
this.suggestions,
this.mentionValue,
required this.onMentionValueChanged,
});
final String? initialValue;
final void Function(String)? onChanged;
final void Function(String)? onFieldSubmitted;
final TextStyle? style;
final InputDecoration? decoration;
final List<(String, Object?, Widget?)>? initialMentions;
final List<EnvironmentVariableSuggestion>? suggestions;
final String? mentionValue;
final void Function(String?) onMentionValueChanged;
@override
State<EnvironmentFieldBase> createState() =>
_EnvironmentAutocompleteFieldBaseState();
}
class _EnvironmentAutocompleteFieldBaseState
extends State<EnvironmentFieldBase> {
final MentionTagTextEditingController controller =
MentionTagTextEditingController();
final FocusNode focusNode = FocusNode();
@override
void initState() {
super.initState();
controller.text = widget.initialValue ?? "";
}
@override
Widget build(BuildContext context) {
final isSuggestionsVisible = useState(false);
return PortalTarget(
visible: isSuggestionsVisible.value && focusNode.hasFocus,
portalFollower: SuggestionsMenu(
mentionController: controller,
suggestions: widget.suggestions,
suggestionBuilder: (context, index) {
final suggestion = widget.suggestions![index];
return ListTile(
dense: true,
leading: EnvVarIndicator(suggestion: suggestion),
title: Text(suggestion.variable.key),
subtitle: Text(suggestion.variable.value),
onTap: () {
controller.addMention(
label: '{${suggestion.variable.key}}}',
data: suggestion,
stylingWidget: EnvVarSpan(suggestion: suggestion));
widget.onChanged?.call(controller.text);
widget.onMentionValueChanged.call(null);
isSuggestionsVisible.value = false;
},
);
},
),
anchor: const Aligned(
follower: Alignment.topLeft,
target: Alignment.bottomLeft,
backup: Aligned(
follower: Alignment.bottomLeft,
target: Alignment.topLeft,
),
),
child: MentionField(
focusNode: focusNode,
controller: controller,
initialMentions: widget.initialMentions ?? [],
mentionValue: widget.mentionValue,
onMentionValueChanged: widget.onMentionValueChanged,
isSuggestionsVisible: isSuggestionsVisible,
onChanged: widget.onChanged,
onFieldSubmitted: widget.onFieldSubmitted,
style: widget.style,
decoration: widget.decoration,
mentionStart: const ['{'],
maxWords: 1,
),
);
}
}

View File

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
class CellField extends StatelessWidget {
const CellField({
super.key,
required this.keyId,
this.initialValue,
this.hintText,
this.onChanged,
this.colorScheme,
});
final String keyId;
final String? initialValue;
final String? hintText;
final void Function(String)? onChanged;
final ColorScheme? colorScheme;
@override
Widget build(BuildContext context) {
var clrScheme = colorScheme ?? Theme.of(context).colorScheme;
return TextFormField(
key: Key(keyId),
initialValue: initialValue,
style: kCodeStyle.copyWith(
color: clrScheme.onSurface,
),
decoration: InputDecoration(
hintStyle: kCodeStyle.copyWith(
color: clrScheme.outline.withOpacity(
kHintOpacity,
),
),
hintText: hintText,
contentPadding: const EdgeInsets.only(bottom: 12),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: clrScheme.primary.withOpacity(
kHintOpacity,
),
),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: clrScheme.surfaceVariant,
),
),
),
onChanged: onChanged,
);
}
}

View File

@ -0,0 +1,67 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class ObscurableCellField extends HookWidget {
const ObscurableCellField({
super.key,
required this.keyId,
this.initialValue,
this.hintText,
this.onChanged,
this.colorScheme,
});
final String keyId;
final String? initialValue;
final String? hintText;
final void Function(String)? onChanged;
final ColorScheme? colorScheme;
@override
Widget build(BuildContext context) {
final obscureText = useState(true);
var clrScheme = colorScheme ?? Theme.of(context).colorScheme;
return TextFormField(
key: Key(keyId),
initialValue: initialValue,
style: kCodeStyle.copyWith(
color: clrScheme.onSurface,
),
obscureText: obscureText.value,
decoration: InputDecoration(
hintStyle: kCodeStyle.copyWith(
color: clrScheme.outline.withOpacity(
kHintOpacity,
),
),
hintText: hintText,
suffixIcon: IconButton(
padding: kP4,
icon: Icon(
obscureText.value ? Icons.visibility : Icons.visibility_off,
color: clrScheme.onSurface,
size: 14,
),
onPressed: () {
obscureText.value = !obscureText.value;
},
),
contentPadding: const EdgeInsets.only(bottom: 12),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: clrScheme.primary.withOpacity(
kHintOpacity,
),
),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: clrScheme.surfaceVariant,
),
),
),
onChanged: onChanged,
);
}
}

View File

@ -1,7 +1,7 @@
import 'package:apidash/consts.dart';
import 'package:apidash/utils/header_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:apidash/utils/utils.dart';
import 'package:apidash/consts.dart';
class HeaderField extends StatefulWidget {
const HeaderField({

View File

@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
import 'field_raw.dart';
class JsonSearchField extends StatelessWidget {
const JsonSearchField({super.key, this.onChanged, this.controller});
final void Function(String)? onChanged;
final TextEditingController? controller;
@override
Widget build(BuildContext context) {
return RawTextField(
controller: controller,
onChanged: onChanged,
style: kCodeStyle,
hintText: 'Search..',
);
}
}

View File

@ -0,0 +1,84 @@
import 'package:flutter/material.dart';
import 'package:mention_tag_text_field/mention_tag_text_field.dart';
class MentionField extends StatelessWidget {
const MentionField({
super.key,
required this.focusNode,
required this.controller,
required this.initialMentions,
required this.mentionValue,
required this.onMentionValueChanged,
required this.isSuggestionsVisible,
this.onChanged,
this.onFieldSubmitted,
this.style,
this.decoration,
required this.mentionStart,
this.mentionBreak = "",
this.maxWords,
this.allowDecrement = false,
this.allowEmbedding = true,
this.showMentionStartSymbol = false,
});
final FocusNode focusNode;
final MentionTagTextEditingController controller;
final List<(String, Object?, Widget?)> initialMentions;
final String? mentionValue;
final void Function(String?) onMentionValueChanged;
final ValueNotifier<bool> isSuggestionsVisible;
final void Function(String)? onChanged;
final void Function(String)? onFieldSubmitted;
final TextStyle? style;
final InputDecoration? decoration;
final List<String> mentionStart;
final String mentionBreak;
final int? maxWords;
final bool allowDecrement;
final bool allowEmbedding;
final bool showMentionStartSymbol;
void onMention(String? value) {
onMentionValueChanged.call(value);
if (value != null) {
isSuggestionsVisible.value = true;
} else {
isSuggestionsVisible.value = false;
}
}
@override
Widget build(BuildContext context) {
return MentionTagTextFormField(
focusNode: focusNode,
onTap: () {
focusNode.requestFocus();
},
onTapOutside: (event) {
focusNode.unfocus();
isSuggestionsVisible.value = false;
},
controller: controller,
style: style,
initialMentions: initialMentions,
onMention: onMention,
onChanged: (value) {
onChanged?.call(controller.text);
},
onFieldSubmitted: (value) {
onFieldSubmitted?.call(controller.text);
isSuggestionsVisible.value = false;
},
decoration: decoration,
mentionTagDecoration: MentionTagDecoration(
mentionStart: mentionStart,
mentionBreak: mentionBreak,
maxWords: maxWords,
allowDecrement: allowDecrement,
allowEmbedding: allowEmbedding,
showMentionStartSymbol: showMentionStartSymbol,
),
);
}
}

View File

@ -0,0 +1,33 @@
import 'package:flutter/material.dart';
class RawTextField extends StatelessWidget {
const RawTextField({
super.key,
this.onChanged,
this.controller,
this.hintText,
this.style,
});
final void Function(String)? onChanged;
final TextEditingController? controller;
final String? hintText;
final TextStyle? style;
@override
Widget build(BuildContext context) {
return TextField(
controller: controller,
onChanged: onChanged,
style: style,
decoration: InputDecoration(
isDense: true,
border: InputBorder.none,
hintText: hintText,
),
onTapOutside: (PointerDownEvent event) {
FocusManager.instance.primaryFocus?.unfocus();
},
);
}
}

View File

@ -0,0 +1,40 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
class URLField extends StatelessWidget {
const URLField({
super.key,
required this.selectedId,
this.initialValue,
this.onChanged,
this.onFieldSubmitted,
});
final String selectedId;
final String? initialValue;
final void Function(String)? onChanged;
final void Function(String)? onFieldSubmitted;
@override
Widget build(BuildContext context) {
return TextFormField(
key: Key("url-$selectedId"),
initialValue: initialValue,
style: kCodeStyle,
decoration: InputDecoration(
hintText: kHintTextUrlCard,
hintStyle: kCodeStyle.copyWith(
color: Theme.of(context).colorScheme.outline.withOpacity(
kHintOpacity,
),
),
border: InputBorder.none,
),
onChanged: onChanged,
onFieldSubmitted: onFieldSubmitted,
onTapOutside: (PointerDownEvent event) {
FocusManager.instance.primaryFocus?.unfocus();
},
);
}
}

View File

@ -7,7 +7,7 @@ import 'package:url_launcher/url_launcher_string.dart';
import '../consts.dart';
import '../utils/ui_utils.dart';
import "snackbars.dart";
import 'textfields.dart';
import 'field_json_search.dart';
class JsonPreviewerColor {
const JsonPreviewerColor._();

View File

@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:markdown/markdown.dart' as md;
import 'package:url_launcher/url_launcher.dart';
import 'buttons.dart';
import 'button_discord.dart';
import 'button_repo.dart';
class CustomMarkdown extends StatelessWidget {
const CustomMarkdown({

View File

@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:multi_split_view/multi_split_view.dart';
import 'package:apidash/consts.dart';
class DashboardSplitView extends StatefulWidget {
const DashboardSplitView({
super.key,
required this.sidebarWidget,
required this.mainWidget,
});
final Widget sidebarWidget;
final Widget mainWidget;
@override
DashboardSplitViewState createState() => DashboardSplitViewState();
}
class DashboardSplitViewState extends State<DashboardSplitView> {
final MultiSplitViewController _controller = MultiSplitViewController(
areas: [
Area(size: 250, minimalSize: 200),
Area(minimalWeight: 0.7),
],
);
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MultiSplitViewTheme(
data: MultiSplitViewThemeData(
dividerThickness: 3,
dividerPainter: DividerPainters.background(
color: Theme.of(context).colorScheme.surfaceVariant,
highlightedColor: Theme.of(context).colorScheme.outline.withOpacity(
kHintOpacity,
),
animationEnabled: false,
),
),
child: MultiSplitView(
controller: _controller,
children: [
widget.sidebarWidget,
widget.mainWidget,
],
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

View File

@ -1,119 +1,9 @@
import 'package:flutter/material.dart';
import 'package:multi_split_view/multi_split_view.dart';
import 'package:apidash/extensions/extensions.dart';
import 'package:apidash/consts.dart';
class DashboardSplitView extends StatefulWidget {
const DashboardSplitView({
super.key,
required this.sidebarWidget,
required this.mainWidget,
});
final Widget sidebarWidget;
final Widget mainWidget;
@override
DashboardSplitViewState createState() => DashboardSplitViewState();
}
class DashboardSplitViewState extends State<DashboardSplitView> {
final MultiSplitViewController _controller = MultiSplitViewController(
areas: [
Area(size: 250, minimalSize: 200),
Area(minimalWeight: 0.7),
],
);
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return MultiSplitViewTheme(
data: MultiSplitViewThemeData(
dividerThickness: 3,
dividerPainter: DividerPainters.background(
color: Theme.of(context).colorScheme.surfaceVariant,
highlightedColor: Theme.of(context).colorScheme.outline.withOpacity(
kHintOpacity,
),
animationEnabled: false,
),
),
child: MultiSplitView(
controller: _controller,
children: [
widget.sidebarWidget,
widget.mainWidget,
],
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
class EqualSplitView extends StatefulWidget {
const EqualSplitView({
super.key,
required this.leftWidget,
required this.rightWidget,
});
final Widget leftWidget;
final Widget rightWidget;
@override
State<EqualSplitView> createState() => _EqualSplitViewState();
}
class _EqualSplitViewState extends State<EqualSplitView> {
final MultiSplitViewController _controller = MultiSplitViewController(
areas: [
Area(minimalSize: kMinRequestEditorDetailsCardPaneSize),
Area(minimalSize: kMinRequestEditorDetailsCardPaneSize),
],
);
@override
Widget build(BuildContext context) {
return MultiSplitViewTheme(
data: MultiSplitViewThemeData(
dividerThickness: 3,
dividerPainter: DividerPainters.background(
color: Theme.of(context).colorScheme.surfaceVariant,
highlightedColor: Theme.of(context).colorScheme.outline.withOpacity(
kHintOpacity,
),
animationEnabled: false,
),
),
child: MultiSplitView(
controller: _controller,
children: [
widget.leftWidget,
widget.rightWidget,
],
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
class TwoDrawerScaffold extends StatelessWidget {
const TwoDrawerScaffold({
class DrawerSplitView extends StatelessWidget {
const DrawerSplitView({
super.key,
required this.scaffoldKey,
required this.mainContent,

View File

@ -0,0 +1,55 @@
import 'package:flutter/material.dart';
import 'package:multi_split_view/multi_split_view.dart';
import 'package:apidash/consts.dart';
class EqualSplitView extends StatefulWidget {
const EqualSplitView({
super.key,
required this.leftWidget,
required this.rightWidget,
});
final Widget leftWidget;
final Widget rightWidget;
@override
State<EqualSplitView> createState() => _EqualSplitViewState();
}
class _EqualSplitViewState extends State<EqualSplitView> {
final MultiSplitViewController _controller = MultiSplitViewController(
areas: [
Area(minimalSize: kMinRequestEditorDetailsCardPaneSize),
Area(minimalSize: kMinRequestEditorDetailsCardPaneSize),
],
);
@override
Widget build(BuildContext context) {
return MultiSplitViewTheme(
data: MultiSplitViewThemeData(
dividerThickness: 3,
dividerPainter: DividerPainters.background(
color: Theme.of(context).colorScheme.surfaceVariant,
highlightedColor: Theme.of(context).colorScheme.outline.withOpacity(
kHintOpacity,
),
animationEnabled: false,
),
),
child: MultiSplitView(
controller: _controller,
children: [
widget.leftWidget,
widget.rightWidget,
],
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

View File

@ -0,0 +1,53 @@
import 'package:flutter/material.dart';
import 'package:mention_tag_text_field/mention_tag_text_field.dart';
import 'package:apidash/consts.dart';
class SuggestionsMenu extends StatelessWidget {
const SuggestionsMenu({
super.key,
required this.mentionController,
required this.suggestions,
required this.suggestionBuilder,
this.menuWidth = kSuggestionsMenuWidth,
this.menuMaxHeight = kSuggestionsMenuMaxHeight,
});
final MentionTagTextEditingController mentionController;
final List? suggestions;
final double menuWidth;
final double menuMaxHeight;
final Widget? Function(BuildContext, int) suggestionBuilder;
@override
Widget build(BuildContext context) {
return suggestions == null || suggestions!.isEmpty
? const SizedBox.shrink()
: ClipRRect(
borderRadius: kBorderRadius8,
child: Material(
type: MaterialType.card,
elevation: 8,
child: ConstrainedBox(
constraints: BoxConstraints(maxHeight: menuMaxHeight),
child: Ink(
width: menuWidth,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: kBorderRadius8,
border: Border.all(
color: Theme.of(context).colorScheme.outlineVariant,
),
),
child: ListView.separated(
shrinkWrap: true,
itemCount: suggestions?.length ?? 0,
separatorBuilder: (context, index) =>
const Divider(height: 2),
itemBuilder: suggestionBuilder,
),
),
),
),
);
}
}

View File

@ -1,205 +0,0 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
class URLField extends StatelessWidget {
const URLField({
super.key,
required this.selectedId,
this.initialValue,
this.onChanged,
this.onFieldSubmitted,
});
final String selectedId;
final String? initialValue;
final void Function(String)? onChanged;
final void Function(String)? onFieldSubmitted;
@override
Widget build(BuildContext context) {
return TextFormField(
key: Key("url-$selectedId"),
initialValue: initialValue,
style: kCodeStyle,
decoration: InputDecoration(
hintText: kHintTextUrlCard,
hintStyle: kCodeStyle.copyWith(
color: Theme.of(context).colorScheme.outline.withOpacity(
kHintOpacity,
),
),
border: InputBorder.none,
),
onChanged: onChanged,
onFieldSubmitted: onFieldSubmitted,
onTapOutside: (PointerDownEvent event) {
FocusManager.instance.primaryFocus?.unfocus();
},
);
}
}
class CellField extends StatelessWidget {
const CellField({
super.key,
required this.keyId,
this.initialValue,
this.hintText,
this.onChanged,
this.colorScheme,
});
final String keyId;
final String? initialValue;
final String? hintText;
final void Function(String)? onChanged;
final ColorScheme? colorScheme;
@override
Widget build(BuildContext context) {
var clrScheme = colorScheme ?? Theme.of(context).colorScheme;
return TextFormField(
key: Key(keyId),
initialValue: initialValue,
style: kCodeStyle.copyWith(
color: clrScheme.onSurface,
),
decoration: InputDecoration(
hintStyle: kCodeStyle.copyWith(
color: clrScheme.outline.withOpacity(
kHintOpacity,
),
),
hintText: hintText,
contentPadding: const EdgeInsets.only(bottom: 12),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: clrScheme.primary.withOpacity(
kHintOpacity,
),
),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: clrScheme.surfaceVariant,
),
),
),
onChanged: onChanged,
);
}
}
class ObscurableCellField extends HookWidget {
const ObscurableCellField({
super.key,
required this.keyId,
this.initialValue,
this.hintText,
this.onChanged,
this.colorScheme,
});
final String keyId;
final String? initialValue;
final String? hintText;
final void Function(String)? onChanged;
final ColorScheme? colorScheme;
@override
Widget build(BuildContext context) {
final obscureText = useState(true);
var clrScheme = colorScheme ?? Theme.of(context).colorScheme;
return TextFormField(
key: Key(keyId),
initialValue: initialValue,
style: kCodeStyle.copyWith(
color: clrScheme.onSurface,
),
obscureText: obscureText.value,
decoration: InputDecoration(
hintStyle: kCodeStyle.copyWith(
color: clrScheme.outline.withOpacity(
kHintOpacity,
),
),
hintText: hintText,
suffixIcon: IconButton(
padding: kP4,
icon: Icon(
obscureText.value ? Icons.visibility : Icons.visibility_off,
color: clrScheme.onSurface,
size: 14,
),
onPressed: () {
obscureText.value = !obscureText.value;
},
),
contentPadding: const EdgeInsets.only(bottom: 12),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: clrScheme.primary.withOpacity(
kHintOpacity,
),
),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: clrScheme.surfaceVariant,
),
),
),
onChanged: onChanged,
);
}
}
class JsonSearchField extends StatelessWidget {
const JsonSearchField({super.key, this.onChanged, this.controller});
final void Function(String)? onChanged;
final TextEditingController? controller;
@override
Widget build(BuildContext context) {
return RawTextField(
controller: controller,
onChanged: onChanged,
style: kCodeStyle,
hintText: 'Search..',
);
}
}
class RawTextField extends StatelessWidget {
const RawTextField({
super.key,
this.onChanged,
this.controller,
this.hintText,
this.style,
});
final void Function(String)? onChanged;
final TextEditingController? controller;
final String? hintText;
final TextStyle? style;
@override
Widget build(BuildContext context) {
return TextField(
controller: controller,
onChanged: onChanged,
style: style,
decoration: InputDecoration(
isDense: true,
border: InputBorder.none,
hintText: hintText,
),
onTapOutside: (PointerDownEvent event) {
FocusManager.instance.primaryFocus?.unfocus();
},
);
}
}

View File

@ -1,14 +1,31 @@
export 'buttons.dart';
export 'cards.dart';
export 'button_clear_response.dart';
export 'button_copy.dart';
export 'button_discord.dart';
export 'button_repo.dart';
export 'button_save_download.dart';
export 'button_send.dart';
export 'card_request_details.dart';
export 'card_sidebar_environment.dart';
export 'card_sidebar_request.dart';
export 'checkbox.dart';
export 'code_previewer.dart';
export 'codegen_previewer.dart';
export 'dropdowns.dart';
export 'dialogs.dart';
export 'dropdown_codegen.dart';
export 'dropdown_content_type.dart';
export 'dropdown_formdata.dart';
export 'dropdown_http_method.dart';
export 'editor_json.dart';
export 'editor.dart';
export 'environment_field.dart';
export 'environment_field_base.dart';
export 'error_message.dart';
export 'headerfield.dart';
export 'field_cell_obscurable.dart';
export 'field_cell.dart';
export 'field_header.dart';
export 'field_json_search.dart';
export 'field_mention.dart';
export 'field_raw.dart';
export 'field_url.dart';
export 'intro_message.dart';
export 'json_previewer.dart';
export 'markdown.dart';
@ -19,10 +36,12 @@ export 'previewer.dart';
export 'request_widgets.dart';
export 'response_widgets.dart';
export 'snackbars.dart';
export 'splitviews.dart';
export 'splitview_drawer.dart';
export 'splitview_dashboard.dart';
export 'splitview_equal.dart';
export 'suggestions_menu.dart';
export 'tables.dart';
export 'tabs.dart';
export 'textfields.dart';
export 'texts.dart';
export 'uint8_audio_player.dart';
export 'window_caption.dart';