Refactor Intro Screen, Collection Pane & URL Card

This commit is contained in:
Ankit Mahato
2023-04-20 06:51:44 +05:30
parent f370e782f3
commit 67c515d44c
14 changed files with 681 additions and 453 deletions

View File

@ -179,6 +179,8 @@ const kDarkCodeTheme = {
'variable': TextStyle(color: Color(0xffaddb67)),
};
enum RequestItemMenuOption { delete, duplicate }
enum HTTPVerb { get, head, post, put, patch, delete }
enum ContentType { json, text }
@ -311,8 +313,6 @@ const Map<String, String> kCodeHighlighterMap = {
kSubTypeTextYml: "yaml",
};
const sendingIndicator = AssetImage("assets/sending.gif");
// HTTP response status codes
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
const kResponseCodeReasons = {
@ -403,3 +403,9 @@ const kUnexpectedRaiseIssue =
const kRaiseIssue =
"\nPlease raise an issue in API Dash GitHub repo so that we can resolve it.";
const kHintTextUrlCard = "Enter API endpoint like api.foss42.com/country/codes";
const kLabelSend = "Send";
const kLabelSending = "Sending..";
const kLabelBusy = "Busy";
const kLabelCopy = "Copy";

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/providers/providers.dart';
import 'package:apidash/widgets/widgets.dart';
import 'package:apidash/models/models.dart';
import 'package:apidash/utils/utils.dart';
import 'package:apidash/consts.dart';
class CollectionPane extends ConsumerStatefulWidget {
@ -152,8 +152,6 @@ class _RequestListState extends ConsumerState<RequestList> {
}
}
enum RequestItemMenuOption { delete, duplicate }
class RequestItem extends ConsumerStatefulWidget {
const RequestItem({
required this.id,
@ -176,107 +174,27 @@ class _RequestItemState extends ConsumerState<RequestItem> {
@override
Widget build(BuildContext context) {
final Color color = Theme.of(context).colorScheme.surface;
final Color surfaceTint = Theme.of(context).colorScheme.primary;
final activeRequest = ref.watch(activeIdStateProvider);
bool isActiveId = activeRequest == widget.id;
return Material(
borderRadius: kBorderRadius12,
elevation: isActiveId ? 1 : 0,
surfaceTintColor: isActiveId ? surfaceTint : null,
color: color,
type: MaterialType.card,
child: InkWell(
borderRadius: kBorderRadius12,
onTap: () {
ref.read(activeIdStateProvider.notifier).update((state) => widget.id);
},
child: Padding(
padding: EdgeInsets.only(
left: 10,
right: isActiveId ? 0 : 20,
top: 5,
bottom: 5,
),
child: SizedBox(
height: 20,
child: Row(
children: [
MethodBox(method: widget.requestModel.method),
kHSpacer5,
Expanded(
child: Text(
getRequestTitleFromUrl(widget.requestModel.url),
softWrap: false,
overflow: TextOverflow.fade,
),
),
Visibility(
visible: isActiveId,
child: PopupMenuButton<RequestItemMenuOption>(
padding: EdgeInsets.zero,
splashRadius: 14,
iconSize: 14,
onSelected: (RequestItemMenuOption item) {
if (item == RequestItemMenuOption.delete) {
ref
.read(activeIdStateProvider.notifier)
.update((state) => null);
ref
.read(collectionStateNotifierProvider.notifier)
.remove(widget.id);
}
if (item == RequestItemMenuOption.duplicate) {
ref
.read(collectionStateNotifierProvider.notifier)
.duplicate(widget.id);
}
},
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<RequestItemMenuOption>>[
const PopupMenuItem<RequestItemMenuOption>(
value: RequestItemMenuOption.delete,
child: Text('Delete'),
),
const PopupMenuItem<RequestItemMenuOption>(
value: RequestItemMenuOption.duplicate,
child: Text('Duplicate'),
),
],
),
),
],
),
),
),
),
);
}
}
class MethodBox extends StatelessWidget {
const MethodBox({super.key, required this.method});
final HTTPVerb method;
@override
Widget build(BuildContext context) {
String text = method.name.toUpperCase();
if (method == HTTPVerb.delete) {
text = "DEL";
}
return SizedBox(
width: 28,
child: Text(
text,
style: TextStyle(
fontSize: 8,
fontWeight: FontWeight.bold,
color: getHTTPMethodColor(
method,
brightness: Theme.of(context).brightness,
),
),
),
final activeRequestId = ref.watch(activeIdStateProvider);
return SidebarRequestCard(
id: widget.id,
activeRequestId: activeRequestId,
url: widget.requestModel.url,
method: widget.requestModel.method,
onTap: () {
ref.read(activeIdStateProvider.notifier).update((state) => widget.id);
},
onMenuSelected: (RequestItemMenuOption item) {
if (item == RequestItemMenuOption.delete) {
ref.read(activeIdStateProvider.notifier).update((state) => null);
ref.read(collectionStateNotifierProvider.notifier).remove(widget.id);
}
if (item == RequestItemMenuOption.duplicate) {
ref
.read(collectionStateNotifierProvider.notifier)
.duplicate(widget.id);
}
},
);
}
}

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:apidash/providers/providers.dart';
import 'package:apidash/widgets/widgets.dart';
import 'package:apidash/consts.dart';
import 'details_card/details_card.dart';
import 'url_card.dart';
@ -49,233 +49,16 @@ class RequestEditorPaneHome extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final isDarkMode = ref.watch(darkModeProvider);
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 40,
horizontal: 60,
),
child: ListView(
children: [
Row(
children: [
Expanded(
child: Text(
"Welcome to API Dash ⚡️",
style: Theme.of(context).textTheme.headlineLarge,
),
),
],
),
kVSpacer20,
Row(
children: [
Expanded(
child: Text(
kIntro,
style: Theme.of(context).textTheme.titleMedium,
),
),
],
),
kVSpacer10,
Row(
children: [
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: "Please support this project by giving us a ",
style: Theme.of(context).textTheme.titleMedium,
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: FilledButton.icon(
onPressed: () {
launchUrl(Uri.parse(kGitUrl));
},
icon: const Icon(Icons.star),
label: const Text(
'Star on GitHub',
style: kTextStyleButton,
),
),
),
],
),
),
),
],
),
kVSpacer20,
Row(
children: [
Expanded(
child: Text(
"Getting Started",
style: Theme.of(context).textTheme.headlineSmall,
),
),
],
),
kVSpacer20,
Row(
children: [
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: "Click on the ",
style: Theme.of(context).textTheme.titleMedium,
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: ElevatedButton(
onPressed: () {
String newId = ref
.read(collectionStateNotifierProvider.notifier)
.add();
ref
.read(activeIdStateProvider.notifier)
.update((state) => newId);
},
child: const Text(
'+ New',
style: kTextStyleButton,
),
),
),
TextSpan(
text: " button to start drafting a new API request.",
style: Theme.of(context).textTheme.titleMedium,
),
],
),
),
),
],
),
kVSpacer10,
Row(
children: [
Text.rich(
TextSpan(
children: [
const TextSpan(
text: "Choose your theme now: ",
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: ElevatedButton.icon(
onPressed: () async {
await ref.read(darkModeProvider.notifier).toggle();
},
icon: isDarkMode
? const Icon(Icons.dark_mode)
: const Icon(Icons.light_mode),
label: Text(
isDarkMode ? "Dark" : "Light",
style: kTextStyleButton,
),
),
),
],
),
style: Theme.of(context).textTheme.titleMedium,
),
],
),
kVSpacer20,
kVSpacer10,
Row(
children: [
Expanded(
child: Text(
"Support Channel",
style: Theme.of(context).textTheme.headlineSmall,
),
),
],
),
kVSpacer20,
Row(
children: [
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: "Join our ",
style: Theme.of(context).textTheme.titleMedium,
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: FilledButton.icon(
onPressed: () {
launchUrl(Uri.parse(kDiscordUrl));
},
icon: const Icon(Icons.discord),
label: const Text(
'Discord Server',
style: kTextStyleButton,
),
),
),
TextSpan(
text: " and drop a message in the #foss channel.",
style: Theme.of(context).textTheme.titleMedium,
),
],
),
),
),
],
),
kVSpacer20,
kVSpacer10,
Row(
children: [
Expanded(
child: Text(
"Report Bug / Request New Feature",
style: Theme.of(context).textTheme.headlineSmall,
),
),
],
),
kVSpacer20,
Row(
children: [
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: "Just raise an issue in our ",
style: Theme.of(context).textTheme.titleMedium,
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: FilledButton.icon(
onPressed: () {
launchUrl(Uri.parse(kGitUrl));
},
icon: const Icon(Icons.code_rounded),
label: const Text(
'Github Repo',
style: kTextStyleButton,
),
),
),
],
),
),
),
],
),
],
),
return IntroMessage(
isDarkMode: isDarkMode,
onNew: () {
String newId = ref.read(collectionStateNotifierProvider.notifier).add();
ref.read(activeIdStateProvider.notifier).update((state) => newId);
},
onModeToggle: () async {
await ref.read(darkModeProvider.notifier).toggle();
},
);
}
}

View File

@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/providers/providers.dart';
import 'package:apidash/utils/utils.dart';
import 'package:apidash/widgets/widgets.dart';
import 'package:apidash/consts.dart';
class EditorPaneRequestURLCard extends StatefulWidget {
@ -43,7 +43,7 @@ class _EditorPaneRequestURLCardState extends State<EditorPaneRequestURLCard> {
kHSpacer20,
SizedBox(
height: 36,
child: SendRequestButton(),
child: SendButton(),
),
],
),
@ -71,42 +71,16 @@ class _DropdownButtonHTTPMethodState
@override
Widget build(BuildContext context) {
final surfaceColor = Theme.of(context).colorScheme.surface;
final activeId = ref.watch(activeIdStateProvider);
final method =
ref.watch(activeRequestModelProvider.select((value) => value?.method));
return DropdownButton<HTTPVerb>(
focusColor: surfaceColor,
value: method,
icon: const Icon(Icons.unfold_more_rounded),
elevation: 4,
underline: Container(
height: 0,
),
borderRadius: kBorderRadius12,
return DropdownButtonHttpMethod(
method: method,
onChanged: (HTTPVerb? value) {
ref
.read(collectionStateNotifierProvider.notifier)
.update(activeId!, method: value);
},
items: HTTPVerb.values.map<DropdownMenuItem<HTTPVerb>>((HTTPVerb value) {
return DropdownMenuItem<HTTPVerb>(
value: value,
child: Padding(
padding: const EdgeInsets.only(left: 16),
child: Text(
value.name.toUpperCase(),
style: kCodeStyle.copyWith(
fontWeight: FontWeight.bold,
color: getHTTPMethodColor(
value,
brightness: Theme.of(context).brightness,
),
),
),
),
);
}).toList(),
);
}
}
@ -129,22 +103,12 @@ class _URLTextFieldState extends ConsumerState<URLTextField> {
@override
Widget build(BuildContext context) {
final activeId = ref.watch(activeIdStateProvider);
return TextFormField(
key: Key("url-${activeId!}"),
return URLField(
activeId: activeId!,
initialValue: ref
.read(collectionStateNotifierProvider.notifier)
.getRequestModel(activeId)
.url,
style: kCodeStyle,
decoration: InputDecoration(
hintText: "Enter API endpoint like api.foss42.com/country/codes",
hintStyle: kCodeStyle.copyWith(
color: Theme.of(context).colorScheme.outline.withOpacity(
kHintOpacity,
),
),
border: InputBorder.none,
),
onChanged: (value) {
ref
.read(collectionStateNotifierProvider.notifier)
@ -154,16 +118,16 @@ class _URLTextFieldState extends ConsumerState<URLTextField> {
}
}
class SendRequestButton extends ConsumerStatefulWidget {
const SendRequestButton({
class SendButton extends ConsumerStatefulWidget {
const SendButton({
super.key,
});
@override
ConsumerState<SendRequestButton> createState() => _SendRequestButtonState();
ConsumerState<SendButton> createState() => _SendButtonState();
}
class _SendRequestButtonState extends ConsumerState<SendRequestButton> {
class _SendButtonState extends ConsumerState<SendButton> {
@override
void initState() {
super.initState();
@ -173,31 +137,14 @@ class _SendRequestButtonState extends ConsumerState<SendRequestButton> {
Widget build(BuildContext context) {
final activeId = ref.watch(activeIdStateProvider);
final sentRequestId = ref.watch(sentRequestIdStateProvider);
bool disable = sentRequestId != null;
return FilledButton(
onPressed: disable
? null
: () {
ref
.read(collectionStateNotifierProvider.notifier)
.sendRequest(activeId!);
},
child: Row(
children: [
Text(
disable
? (activeId == sentRequestId ? "Sending.." : "Busy")
: "Send",
style: kTextStyleButton,
),
if (!disable) kHSpacer10,
if (!disable)
const Icon(
size: 16,
Icons.send,
),
],
),
return SendRequestButton(
activeId: activeId,
sentRequestId: sentRequestId,
onTap: () {
ref
.read(collectionStateNotifierProvider.notifier)
.sendRequest(activeId!);
},
);
}
}

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:multi_split_view/multi_split_view.dart';
import 'package:apidash/consts.dart';
import 'package:apidash/widgets/widgets.dart';
import 'editor_pane/editor_pane.dart';
import 'collection_pane.dart';
@ -12,53 +11,11 @@ class HomePage extends StatefulWidget {
}
class HomePageState extends State<HomePage> {
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 Scaffold(
body: Column(
children: [
Expanded(
child: 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: const [
CollectionPane(),
RequestEditorPane(),
],
),
),
),
],
),
return const DashboardSplitView(
sidebarWidget: CollectionPane(),
mainWidget: RequestEditorPane(),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:apidash/consts.dart';
class CopyButton extends StatefulWidget {
const CopyButton({super.key, required this.toCopy});
@ -22,7 +23,56 @@ class _CopyButtonState extends State<CopyButton> {
Icons.content_copy,
size: 20,
),
Text("Copy")
Text(kLabelCopy)
],
),
);
}
}
class SendRequestButton extends StatefulWidget {
const SendRequestButton({
super.key,
required this.activeId,
required this.sentRequestId,
required this.onTap,
});
final String? activeId;
final String? sentRequestId;
final void Function() onTap;
@override
State<SendRequestButton> createState() => _SendRequestButtonState();
}
class _SendRequestButtonState extends State<SendRequestButton> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
bool disable = widget.sentRequestId != null;
return FilledButton(
onPressed: disable ? null : widget.onTap,
child: Row(
children: [
Text(
disable
? (widget.activeId == widget.sentRequestId
? kLabelSending
: kLabelBusy)
: kLabelSend,
style: kTextStyleButton,
),
if (!disable) kHSpacer10,
if (!disable)
const Icon(
size: 16,
Icons.send,
),
],
),
);

77
lib/widgets/cards.dart Normal file
View File

@ -0,0 +1,77 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
import 'package:apidash/utils/utils.dart';
import 'menus.dart' show RequestCardMenu;
import 'texts.dart' show MethodBox;
class SidebarRequestCard extends StatefulWidget {
const SidebarRequestCard({
super.key,
required this.id,
required this.activeRequestId,
required this.url,
required this.method,
this.onTap,
this.onMenuSelected,
});
final String id;
final String? activeRequestId;
final String url;
final HTTPVerb method;
final void Function()? onTap;
final Function(RequestItemMenuOption)? onMenuSelected;
@override
State<SidebarRequestCard> createState() => _SidebarRequestCardState();
}
class _SidebarRequestCardState extends State<SidebarRequestCard> {
@override
Widget build(BuildContext context) {
final Color color = Theme.of(context).colorScheme.surface;
final Color surfaceTint = Theme.of(context).colorScheme.primary;
bool isActiveId = widget.activeRequestId == widget.id;
return Material(
borderRadius: kBorderRadius12,
elevation: isActiveId ? 1 : 0,
surfaceTintColor: isActiveId ? surfaceTint : null,
color: color,
type: MaterialType.card,
child: InkWell(
borderRadius: kBorderRadius12,
onTap: widget.onTap,
child: Padding(
padding: EdgeInsets.only(
left: 10,
right: isActiveId ? 0 : 20,
top: 5,
bottom: 5,
),
child: SizedBox(
height: 20,
child: Row(
children: [
MethodBox(method: widget.method),
kHSpacer5,
Expanded(
child: Text(
getRequestTitleFromUrl(widget.url),
softWrap: false,
overflow: TextOverflow.fade,
),
),
Visibility(
visible: isActiveId,
child: RequestCardMenu(
onSelected: widget.onMenuSelected,
),
),
],
),
),
),
),
);
}
}

View File

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

View File

@ -0,0 +1,244 @@
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:apidash/consts.dart';
class IntroMessage extends StatefulWidget {
const IntroMessage({
super.key,
required this.isDarkMode,
this.onNew,
this.onModeToggle,
});
final bool isDarkMode;
final void Function()? onNew;
final void Function()? onModeToggle;
@override
State<IntroMessage> createState() => _IntroMessageState();
}
class _IntroMessageState extends State<IntroMessage> {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(
vertical: 40,
horizontal: 60,
),
child: ListView(
children: [
Row(
children: [
Expanded(
child: Text(
"Welcome to API Dash ⚡️",
style: Theme.of(context).textTheme.headlineLarge,
),
),
],
),
kVSpacer20,
Row(
children: [
Expanded(
child: Text(
kIntro,
style: Theme.of(context).textTheme.titleMedium,
),
),
],
),
kVSpacer10,
Row(
children: [
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: "Please support this project by giving us a ",
style: Theme.of(context).textTheme.titleMedium,
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: FilledButton.icon(
onPressed: () {
launchUrl(Uri.parse(kGitUrl));
},
icon: const Icon(Icons.star),
label: const Text(
'Star on GitHub',
style: kTextStyleButton,
),
),
),
],
),
),
),
],
),
kVSpacer20,
Row(
children: [
Expanded(
child: Text(
"Getting Started",
style: Theme.of(context).textTheme.headlineSmall,
),
),
],
),
kVSpacer20,
Row(
children: [
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: "Click on the ",
style: Theme.of(context).textTheme.titleMedium,
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: ElevatedButton(
onPressed: widget.onNew,
child: const Text(
'+ New',
style: kTextStyleButton,
),
),
),
TextSpan(
text: " button to start drafting a new API request.",
style: Theme.of(context).textTheme.titleMedium,
),
],
),
),
),
],
),
kVSpacer10,
Row(
children: [
Text.rich(
TextSpan(
children: [
const TextSpan(
text: "Choose your theme now: ",
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: ElevatedButton.icon(
onPressed: widget.onModeToggle,
icon: widget.isDarkMode
? const Icon(Icons.dark_mode)
: const Icon(Icons.light_mode),
label: Text(
widget.isDarkMode ? "Dark" : "Light",
style: kTextStyleButton,
),
),
),
],
),
style: Theme.of(context).textTheme.titleMedium,
),
],
),
kVSpacer20,
kVSpacer10,
Row(
children: [
Expanded(
child: Text(
"Support Channel",
style: Theme.of(context).textTheme.headlineSmall,
),
),
],
),
kVSpacer20,
Row(
children: [
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: "Join our ",
style: Theme.of(context).textTheme.titleMedium,
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: FilledButton.icon(
onPressed: () {
launchUrl(Uri.parse(kDiscordUrl));
},
icon: const Icon(Icons.discord),
label: const Text(
'Discord Server',
style: kTextStyleButton,
),
),
),
TextSpan(
text: " and drop a message in the #foss channel.",
style: Theme.of(context).textTheme.titleMedium,
),
],
),
),
),
],
),
kVSpacer20,
kVSpacer10,
Row(
children: [
Expanded(
child: Text(
"Report Bug / Request New Feature",
style: Theme.of(context).textTheme.headlineSmall,
),
),
],
),
kVSpacer20,
Row(
children: [
Expanded(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: "Just raise an issue in our ",
style: Theme.of(context).textTheme.titleMedium,
),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: FilledButton.icon(
onPressed: () {
launchUrl(Uri.parse(kGitUrl));
},
icon: const Icon(Icons.code_rounded),
label: const Text(
'Github Repo',
style: kTextStyleButton,
),
),
),
],
),
),
),
],
),
],
),
);
}
}

37
lib/widgets/menus.dart Normal file
View File

@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
class RequestCardMenu extends StatefulWidget {
const RequestCardMenu({
super.key,
this.onSelected,
});
final Function(RequestItemMenuOption)? onSelected;
@override
State<RequestCardMenu> createState() => _RequestCardMenuState();
}
class _RequestCardMenuState extends State<RequestCardMenu> {
@override
Widget build(BuildContext context) {
return PopupMenuButton<RequestItemMenuOption>(
padding: EdgeInsets.zero,
splashRadius: 14,
iconSize: 14,
onSelected: widget.onSelected,
itemBuilder: (BuildContext context) =>
<PopupMenuEntry<RequestItemMenuOption>>[
const PopupMenuItem<RequestItemMenuOption>(
value: RequestItemMenuOption.delete,
child: Text('Delete'),
),
const PopupMenuItem<RequestItemMenuOption>(
value: RequestItemMenuOption.duplicate,
child: Text('Duplicate'),
),
],
);
}
}

View File

@ -0,0 +1,69 @@
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({
Key? key,
required this.sidebarWidget,
required this.mainWidget,
}) : super(key: key);
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 Scaffold(
body: Column(
children: [
Expanded(
child: 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

@ -0,0 +1,44 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
class URLField extends StatefulWidget {
const URLField({
super.key,
required this.activeId,
this.initialValue,
this.onChanged,
});
final String activeId;
final String? initialValue;
final void Function(String)? onChanged;
@override
State<URLField> createState() => _URLFieldState();
}
class _URLFieldState extends State<URLField> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return TextFormField(
key: Key("url-${widget.activeId}"),
initialValue: widget.initialValue,
style: kCodeStyle,
decoration: InputDecoration(
hintText: kHintTextUrlCard,
hintStyle: kCodeStyle.copyWith(
color: Theme.of(context).colorScheme.outline.withOpacity(
kHintOpacity,
),
),
border: InputBorder.none,
),
onChanged: widget.onChanged,
);
}
}

30
lib/widgets/texts.dart Normal file
View File

@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:apidash/utils/utils.dart';
import 'package:apidash/consts.dart';
class MethodBox extends StatelessWidget {
const MethodBox({super.key, required this.method});
final HTTPVerb method;
@override
Widget build(BuildContext context) {
String text = method.name.toUpperCase();
if (method == HTTPVerb.delete) {
text = "DEL";
}
return SizedBox(
width: 28,
child: Text(
text,
style: TextStyle(
fontSize: 8,
fontWeight: FontWeight.bold,
color: getHTTPMethodColor(
method,
brightness: Theme.of(context).brightness,
),
),
),
);
}
}

View File

@ -6,3 +6,10 @@ export 'code_previewer.dart';
export 'codegen_previewer.dart';
export 'error_message.dart';
export 'sending_widget.dart';
export 'dropdowns.dart';
export 'splitviews.dart';
export 'texts.dart';
export 'textfields.dart';
export 'menus.dart';
export 'cards.dart';
export 'intro_message.dart';