mirror of
https://github.com/foss42/apidash.git
synced 2025-08-06 13:51:20 +08:00
wip: history panes
This commit is contained in:
@ -9,6 +9,7 @@ final navRailIndexStateProvider = StateProvider<int>((ref) => 0);
|
|||||||
final selectedIdEditStateProvider = StateProvider<String?>((ref) => null);
|
final selectedIdEditStateProvider = StateProvider<String?>((ref) => null);
|
||||||
final environmentFieldEditStateProvider = StateProvider<String?>((ref) => null);
|
final environmentFieldEditStateProvider = StateProvider<String?>((ref) => null);
|
||||||
final codePaneVisibleStateProvider = StateProvider<bool>((ref) => false);
|
final codePaneVisibleStateProvider = StateProvider<bool>((ref) => false);
|
||||||
|
final historyCodePaneVisibleStateProvider = StateProvider<bool>((ref) => false);
|
||||||
final saveDataStateProvider = StateProvider<bool>((ref) => false);
|
final saveDataStateProvider = StateProvider<bool>((ref) => false);
|
||||||
final clearDataStateProvider = StateProvider<bool>((ref) => false);
|
final clearDataStateProvider = StateProvider<bool>((ref) => false);
|
||||||
final hasUnsavedChangesProvider = StateProvider<bool>((ref) => false);
|
final hasUnsavedChangesProvider = StateProvider<bool>((ref) => false);
|
||||||
|
@ -9,14 +9,24 @@ import 'package:apidash/consts.dart';
|
|||||||
final Codegen codegen = Codegen();
|
final Codegen codegen = Codegen();
|
||||||
|
|
||||||
class CodePane extends ConsumerWidget {
|
class CodePane extends ConsumerWidget {
|
||||||
const CodePane({super.key});
|
const CodePane({
|
||||||
|
super.key,
|
||||||
|
this.isHistoryRequest = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
final bool isHistoryRequest;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final CodegenLanguage codegenLanguage =
|
final CodegenLanguage codegenLanguage =
|
||||||
ref.watch(codegenLanguageStateProvider);
|
ref.watch(codegenLanguageStateProvider);
|
||||||
|
|
||||||
final selectedRequestModel = ref.watch(selectedRequestModelProvider);
|
final selectedHistoryRequestModel =
|
||||||
|
ref.watch(selectedHistoryRequestModelProvider);
|
||||||
|
|
||||||
|
final selectedRequestModel = isHistoryRequest
|
||||||
|
? getRequestModelFromHistoryModel(selectedHistoryRequestModel!)
|
||||||
|
: ref.watch(selectedRequestModelProvider);
|
||||||
final defaultUriScheme =
|
final defaultUriScheme =
|
||||||
ref.watch(settingsProvider.select((value) => value.defaultUriScheme));
|
ref.watch(settingsProvider.select((value) => value.defaultUriScheme));
|
||||||
|
|
@ -1,4 +1,5 @@
|
|||||||
export 'button_navbar.dart';
|
export 'button_navbar.dart';
|
||||||
|
export 'code_pane.dart';
|
||||||
export 'editor_title.dart';
|
export 'editor_title.dart';
|
||||||
export 'editor_title_actions.dart';
|
export 'editor_title_actions.dart';
|
||||||
export 'envfield_url.dart';
|
export 'envfield_url.dart';
|
||||||
|
60
lib/screens/history/details_pane/his_request_pane.dart
Normal file
60
lib/screens/history/details_pane/his_request_pane.dart
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
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/consts.dart';
|
||||||
|
|
||||||
|
class HistoryRequestPane extends ConsumerWidget {
|
||||||
|
const HistoryRequestPane({
|
||||||
|
super.key,
|
||||||
|
this.isCompact = false,
|
||||||
|
});
|
||||||
|
|
||||||
|
final bool isCompact;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final selectedId = ref.watch(selectedHistoryIdStateProvider);
|
||||||
|
final codePaneVisible = ref.watch(historyCodePaneVisibleStateProvider);
|
||||||
|
|
||||||
|
final headersMap = ref.watch(selectedHistoryRequestModelProvider
|
||||||
|
.select((value) => value?.httpRequestModel.headersMap)) ??
|
||||||
|
{};
|
||||||
|
final headerLength = headersMap.length;
|
||||||
|
|
||||||
|
final paramsMap = ref.watch(selectedHistoryRequestModelProvider
|
||||||
|
.select((value) => value?.httpRequestModel.paramsMap)) ??
|
||||||
|
{};
|
||||||
|
final paramLength = paramsMap.length;
|
||||||
|
|
||||||
|
final hasBody = ref.watch(selectedHistoryRequestModelProvider
|
||||||
|
.select((value) => value?.httpRequestModel.hasBody)) ??
|
||||||
|
false;
|
||||||
|
|
||||||
|
return RequestPane(
|
||||||
|
selectedId: selectedId,
|
||||||
|
codePaneVisible: codePaneVisible,
|
||||||
|
onPressedCodeButton: () {
|
||||||
|
ref.read(historyCodePaneVisibleStateProvider.notifier).state =
|
||||||
|
!codePaneVisible;
|
||||||
|
},
|
||||||
|
showViewCodeButton: !isCompact,
|
||||||
|
showIndicators: [
|
||||||
|
paramLength > 0,
|
||||||
|
headerLength > 0,
|
||||||
|
hasBody,
|
||||||
|
],
|
||||||
|
children: [
|
||||||
|
RequestDataTable(
|
||||||
|
rows: paramsMap,
|
||||||
|
keyName: kNameURLParam,
|
||||||
|
),
|
||||||
|
RequestDataTable(
|
||||||
|
rows: headersMap,
|
||||||
|
keyName: kNameHeader,
|
||||||
|
),
|
||||||
|
const SizedBox(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
48
lib/screens/history/details_pane/his_response_pane.dart
Normal file
48
lib/screens/history/details_pane/his_response_pane.dart
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:apidash/providers/providers.dart';
|
||||||
|
import 'package:apidash/widgets/widgets.dart';
|
||||||
|
import 'package:apidash/utils/utils.dart';
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
|
|
||||||
|
class HistoryResponsePane extends ConsumerWidget {
|
||||||
|
const HistoryResponsePane({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final selectedId = ref.watch(selectedHistoryIdStateProvider);
|
||||||
|
final selectedHistoryRequest =
|
||||||
|
ref.watch(selectedHistoryRequestModelProvider);
|
||||||
|
final historyHttpResponseModel = selectedHistoryRequest?.httpResponseModel;
|
||||||
|
|
||||||
|
if (selectedId != null) {
|
||||||
|
final requestModel =
|
||||||
|
getRequestModelFromHistoryModel(selectedHistoryRequest!);
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
ResponsePaneHeader(
|
||||||
|
responseStatus: historyHttpResponseModel?.statusCode,
|
||||||
|
message: kResponseCodeReasons[historyHttpResponseModel?.statusCode],
|
||||||
|
time: historyHttpResponseModel?.time,
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: ResponseTabView(
|
||||||
|
selectedId: selectedId,
|
||||||
|
children: [
|
||||||
|
ResponseBody(
|
||||||
|
selectedRequestModel: requestModel,
|
||||||
|
),
|
||||||
|
ResponseHeaders(
|
||||||
|
responseHeaders: historyHttpResponseModel?.headers ?? {},
|
||||||
|
requestHeaders:
|
||||||
|
historyHttpResponseModel?.requestHeaders ?? {},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return const Text("No Request Selected");
|
||||||
|
}
|
||||||
|
}
|
@ -49,9 +49,8 @@ class HistoryURLCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
isCompact ? kHSpacer10 : kHSpacer20,
|
isCompact ? kHSpacer10 : kHSpacer20,
|
||||||
Expanded(
|
Expanded(
|
||||||
child: RawTextField(
|
child: ReadOnlyTextField(
|
||||||
readOnly: true,
|
initialValue: url,
|
||||||
controller: TextEditingController(text: url),
|
|
||||||
style: kCodeStyle.copyWith(
|
style: kCodeStyle.copyWith(
|
||||||
fontSize: fontSize,
|
fontSize: fontSize,
|
||||||
),
|
),
|
||||||
|
@ -4,7 +4,10 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:apidash/providers/providers.dart';
|
import 'package:apidash/providers/providers.dart';
|
||||||
import 'package:apidash/widgets/widgets.dart';
|
import 'package:apidash/widgets/widgets.dart';
|
||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
import './details_pane/url_card.dart';
|
import 'package:apidash/screens/common_widgets/common_widgets.dart';
|
||||||
|
import 'details_pane/url_card.dart';
|
||||||
|
import 'details_pane/his_request_pane.dart';
|
||||||
|
import 'details_pane/his_response_pane.dart';
|
||||||
|
|
||||||
class HistoryDetails extends StatefulHookConsumerWidget {
|
class HistoryDetails extends StatefulHookConsumerWidget {
|
||||||
const HistoryDetails({super.key});
|
const HistoryDetails({super.key});
|
||||||
@ -21,22 +24,62 @@ class _HistoryDetailsState extends ConsumerState<HistoryDetails>
|
|||||||
ref.watch(selectedHistoryRequestModelProvider);
|
ref.watch(selectedHistoryRequestModelProvider);
|
||||||
final metaData = selectedHistoryRequest?.metaData;
|
final metaData = selectedHistoryRequest?.metaData;
|
||||||
|
|
||||||
|
final codePaneVisible = ref.watch(historyCodePaneVisibleStateProvider);
|
||||||
|
|
||||||
final TabController controller =
|
final TabController controller =
|
||||||
useTabController(initialLength: 2, vsync: this);
|
useTabController(initialLength: 2, vsync: this);
|
||||||
|
|
||||||
return selectedHistoryRequest != null
|
return selectedHistoryRequest != null
|
||||||
? Column(
|
? LayoutBuilder(
|
||||||
children: [
|
builder: (context, constraints) {
|
||||||
Padding(
|
final isCompact = constraints.maxWidth < kMediumWindowWidth;
|
||||||
padding: kP4,
|
return Column(
|
||||||
child: HistoryURLCard(
|
children: [
|
||||||
method: metaData!.method, url: metaData.url)),
|
kVSpacer5,
|
||||||
kVSpacer10,
|
Padding(
|
||||||
RequestResponseTabbar(
|
padding: kPh4,
|
||||||
controller: controller,
|
child: HistoryURLCard(
|
||||||
),
|
method: metaData!.method,
|
||||||
],
|
url: metaData.url,
|
||||||
|
)),
|
||||||
|
kVSpacer10,
|
||||||
|
if (isCompact) ...[
|
||||||
|
RequestResponseTabbar(
|
||||||
|
controller: controller,
|
||||||
|
),
|
||||||
|
kVSpacer10,
|
||||||
|
Expanded(
|
||||||
|
child: TabBarView(
|
||||||
|
controller: controller,
|
||||||
|
children: [
|
||||||
|
HistoryRequestPane(
|
||||||
|
isCompact: isCompact,
|
||||||
|
),
|
||||||
|
const HistoryResponsePane(),
|
||||||
|
],
|
||||||
|
))
|
||||||
|
] else ...[
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: kPh4,
|
||||||
|
child: RequestDetailsCard(
|
||||||
|
child: EqualSplitView(
|
||||||
|
leftWidget: HistoryRequestPane(
|
||||||
|
isCompact: isCompact,
|
||||||
|
),
|
||||||
|
rightWidget: codePaneVisible
|
||||||
|
? const CodePane(isHistoryRequest: true)
|
||||||
|
: const HistoryResponsePane(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
kVSpacer8,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
)
|
)
|
||||||
: const Text("No Request Selected");
|
: const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import 'package:apidash/widgets/widgets.dart';
|
import 'package:apidash/widgets/widgets.dart';
|
||||||
import 'package:apidash/extensions/extensions.dart';
|
import 'package:apidash/extensions/extensions.dart';
|
||||||
import 'package:apidash/providers/providers.dart';
|
import 'package:apidash/providers/providers.dart';
|
||||||
|
import 'package:apidash/utils/utils.dart';
|
||||||
import 'history_pane.dart';
|
import 'history_pane.dart';
|
||||||
import 'history_viewer.dart';
|
import 'history_viewer.dart';
|
||||||
|
|
||||||
@ -16,11 +17,14 @@ class HistoryPage extends ConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final historyModel = ref.watch(selectedHistoryRequestModelProvider);
|
final historyModel = ref.watch(selectedHistoryRequestModelProvider);
|
||||||
|
final title = historyModel != null
|
||||||
|
? getHistoryRequestName(historyModel.metaData)
|
||||||
|
: 'History';
|
||||||
if (context.isMediumWindow) {
|
if (context.isMediumWindow) {
|
||||||
return DrawerSplitView(
|
return DrawerSplitView(
|
||||||
scaffoldKey: scaffoldKey,
|
scaffoldKey: scaffoldKey,
|
||||||
mainContent: const HistoryViewer(),
|
mainContent: const HistoryViewer(),
|
||||||
title: Text(historyModel?.historyId ?? 'History'),
|
title: Text(title),
|
||||||
leftDrawerContent: const HistoryPane(),
|
leftDrawerContent: const HistoryPane(),
|
||||||
actions: const [SizedBox(width: 16)],
|
actions: const [SizedBox(width: 16)],
|
||||||
onDrawerChanged: (value) =>
|
onDrawerChanged: (value) =>
|
||||||
|
@ -56,6 +56,7 @@ class HistoryList extends HookConsumerWidget {
|
|||||||
title: Text(
|
title: Text(
|
||||||
humanizeDate(date),
|
humanizeDate(date),
|
||||||
),
|
),
|
||||||
|
initiallyExpanded: true,
|
||||||
children: requestGroups.values.map((item) {
|
children: requestGroups.values.map((item) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: kPv2 + kPh4,
|
padding: kPv2 + kPh4,
|
||||||
@ -79,9 +80,11 @@ class HistoryList extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}).toList()
|
}).toList()
|
||||||
: [
|
: [
|
||||||
const Text(
|
const Center(
|
||||||
'No history',
|
child: Text(
|
||||||
style: TextStyle(color: Colors.grey),
|
'No history',
|
||||||
|
style: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:apidash/consts.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:apidash/providers/history_providers.dart';
|
import 'package:apidash/providers/history_providers.dart';
|
||||||
import 'package:apidash/utils/history_utils.dart';
|
import 'package:apidash/utils/history_utils.dart';
|
||||||
@ -10,23 +11,29 @@ class HistoryRequests extends ConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final selectedRequestId = ref.watch(selectedHistoryIdStateProvider);
|
final selectedRequestId = ref.watch(selectedHistoryIdStateProvider);
|
||||||
final selectedRequest = ref.read(selectedHistoryRequestModelProvider);
|
final selectedRequest = ref.watch(selectedHistoryRequestModelProvider);
|
||||||
final historyMetas = ref.read(historyMetaStateNotifier);
|
final historyMetas = ref.watch(historyMetaStateNotifier);
|
||||||
final requestGroup = getRequestGroup(
|
final requestGroup = getRequestGroup(
|
||||||
historyMetas?.values.toList(), selectedRequest?.metaData);
|
historyMetas?.values.toList(), selectedRequest?.metaData);
|
||||||
return Column(
|
return Column(
|
||||||
children: requestGroup
|
children: [
|
||||||
.map((request) => SidebarHistoryCard(
|
kVSpacer20,
|
||||||
|
...requestGroup.map((request) => Padding(
|
||||||
|
padding: kPv2 + kPh4,
|
||||||
|
child: HistoryRequestCard(
|
||||||
id: request.historyId,
|
id: request.historyId,
|
||||||
method: request.method,
|
model: request,
|
||||||
isSelected: selectedRequestId == request.historyId,
|
isSelected: selectedRequestId == request.historyId,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
ref.read(selectedHistoryIdStateProvider.notifier).state =
|
ref.read(selectedHistoryIdStateProvider.notifier).state =
|
||||||
request.historyId;
|
request.historyId;
|
||||||
|
ref
|
||||||
|
.read(historyMetaStateNotifier.notifier)
|
||||||
|
.loadHistoryRequest(request.historyId);
|
||||||
},
|
},
|
||||||
models: [request],
|
),
|
||||||
))
|
))
|
||||||
.toList(),
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:apidash/providers/providers.dart';
|
import 'package:apidash/providers/providers.dart';
|
||||||
import 'package:apidash/widgets/widgets.dart';
|
import 'package:apidash/widgets/widgets.dart';
|
||||||
|
import 'package:apidash/screens/common_widgets/common_widgets.dart';
|
||||||
import 'request_pane/request_pane.dart';
|
import 'request_pane/request_pane.dart';
|
||||||
import 'response_pane.dart';
|
import 'response_pane.dart';
|
||||||
import 'code_pane.dart';
|
|
||||||
|
|
||||||
class EditorPaneRequestDetailsCard extends ConsumerWidget {
|
class EditorPaneRequestDetailsCard extends ConsumerWidget {
|
||||||
const EditorPaneRequestDetailsCard({super.key});
|
const EditorPaneRequestDetailsCard({super.key});
|
||||||
|
@ -7,7 +7,6 @@ import 'package:apidash/widgets/widgets.dart';
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import '../../home_page/collection_pane.dart';
|
import '../../home_page/collection_pane.dart';
|
||||||
import '../../home_page/editor_pane/url_card.dart';
|
import '../../home_page/editor_pane/url_card.dart';
|
||||||
import '../../home_page/editor_pane/details_card/code_pane.dart';
|
|
||||||
import '../../home_page/editor_pane/editor_default.dart';
|
import '../../home_page/editor_pane/editor_default.dart';
|
||||||
import '../../common_widgets/common_widgets.dart';
|
import '../../common_widgets/common_widgets.dart';
|
||||||
import '../widgets/page_base.dart';
|
import '../widgets/page_base.dart';
|
||||||
|
@ -13,6 +13,13 @@ String humanizeDate(DateTime? date) {
|
|||||||
return DateFormat('MMMM d, yyyy').format(date);
|
return DateFormat('MMMM d, yyyy').format(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String humanizeTime(DateTime? time) {
|
||||||
|
if (time == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return DateFormat('hh:mm:ss a').format(time);
|
||||||
|
}
|
||||||
|
|
||||||
String humanizeDuration(Duration? duration) {
|
String humanizeDuration(Duration? duration) {
|
||||||
if (duration == null) {
|
if (duration == null) {
|
||||||
return "";
|
return "";
|
||||||
|
@ -1,10 +1,22 @@
|
|||||||
import 'package:apidash/models/models.dart';
|
|
||||||
import 'package:apidash/utils/convert_utils.dart';
|
import 'package:apidash/utils/convert_utils.dart';
|
||||||
|
import 'package:apidash/models/models.dart';
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
|
|
||||||
DateTime stripTime(DateTime dateTime) {
|
DateTime stripTime(DateTime dateTime) {
|
||||||
return DateTime(dateTime.year, dateTime.month, dateTime.day);
|
return DateTime(dateTime.year, dateTime.month, dateTime.day);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RequestModel getRequestModelFromHistoryModel(HistoryRequestModel model) {
|
||||||
|
return RequestModel(
|
||||||
|
id: model.historyId,
|
||||||
|
name: model.metaData.name,
|
||||||
|
responseStatus: model.httpResponseModel.statusCode,
|
||||||
|
message: kResponseCodeReasons[model.httpResponseModel.statusCode],
|
||||||
|
httpRequestModel: model.httpRequestModel,
|
||||||
|
httpResponseModel: model.httpResponseModel,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
String getHistoryRequestName(HistoryMetaModel model) {
|
String getHistoryRequestName(HistoryMetaModel model) {
|
||||||
if (model.name.isNotEmpty) {
|
if (model.name.isNotEmpty) {
|
||||||
return model.name;
|
return model.name;
|
||||||
|
65
lib/widgets/card_history_request.dart
Normal file
65
lib/widgets/card_history_request.dart
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import 'package:apidash/models/history_meta_model.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
|
import 'package:apidash/utils/utils.dart';
|
||||||
|
import 'texts.dart';
|
||||||
|
|
||||||
|
class HistoryRequestCard extends StatelessWidget {
|
||||||
|
const HistoryRequestCard({
|
||||||
|
super.key,
|
||||||
|
required this.id,
|
||||||
|
required this.model,
|
||||||
|
this.isSelected = false,
|
||||||
|
this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String id;
|
||||||
|
final HistoryMetaModel model;
|
||||||
|
final bool isSelected;
|
||||||
|
final Function()? onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final Color color = Theme.of(context).colorScheme.surface;
|
||||||
|
final Color colorVariant =
|
||||||
|
Theme.of(context).colorScheme.surfaceContainerHighest.withOpacity(0.5);
|
||||||
|
final Color surfaceTint = Theme.of(context).colorScheme.primary;
|
||||||
|
return Card(
|
||||||
|
shape: const ContinuousRectangleBorder(borderRadius: kBorderRadius12),
|
||||||
|
elevation: isSelected ? 1 : 0,
|
||||||
|
surfaceTintColor: isSelected ? surfaceTint : null,
|
||||||
|
color: isSelected
|
||||||
|
? Theme.of(context).colorScheme.brightness == Brightness.dark
|
||||||
|
? colorVariant
|
||||||
|
: color
|
||||||
|
: color,
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
borderRadius: kBorderRadius6,
|
||||||
|
hoverColor: colorVariant,
|
||||||
|
focusColor: colorVariant.withOpacity(0.5),
|
||||||
|
child: Padding(
|
||||||
|
padding: kPv6 + kPh8,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 20,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
humanizeTime(model.timeStamp),
|
||||||
|
softWrap: false,
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
|
style: kCodeStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
kHSpacer4,
|
||||||
|
StatusCode(statusCode: model.responseStatus),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
30
lib/widgets/field_read_only.dart
Normal file
30
lib/widgets/field_read_only.dart
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
|
|
||||||
|
class ReadOnlyTextField extends StatelessWidget {
|
||||||
|
const ReadOnlyTextField({
|
||||||
|
super.key,
|
||||||
|
this.initialValue,
|
||||||
|
this.style,
|
||||||
|
this.decoration,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String? initialValue;
|
||||||
|
final TextStyle? style;
|
||||||
|
final InputDecoration? decoration;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextField(
|
||||||
|
readOnly: true,
|
||||||
|
controller: TextEditingController(text: initialValue),
|
||||||
|
style: style,
|
||||||
|
decoration: decoration ??
|
||||||
|
const InputDecoration(
|
||||||
|
isDense: true,
|
||||||
|
border: InputBorder.none,
|
||||||
|
contentPadding: kPv8,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:apidash/extensions/extensions.dart';
|
||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
import 'tabs.dart';
|
import 'tabs.dart';
|
||||||
import 'package:apidash/extensions/extensions.dart';
|
|
||||||
|
|
||||||
class RequestPane extends StatefulWidget {
|
class RequestPane extends StatefulHookWidget {
|
||||||
const RequestPane({
|
const RequestPane({
|
||||||
super.key,
|
super.key,
|
||||||
required this.selectedId,
|
required this.selectedId,
|
||||||
@ -13,6 +14,7 @@ class RequestPane extends StatefulWidget {
|
|||||||
this.onTapTabBar,
|
this.onTapTabBar,
|
||||||
required this.children,
|
required this.children,
|
||||||
this.showIndicators = const [false, false, false],
|
this.showIndicators = const [false, false, false],
|
||||||
|
this.showViewCodeButton,
|
||||||
});
|
});
|
||||||
|
|
||||||
final String? selectedId;
|
final String? selectedId;
|
||||||
@ -22,6 +24,7 @@ class RequestPane extends StatefulWidget {
|
|||||||
final void Function(int)? onTapTabBar;
|
final void Function(int)? onTapTabBar;
|
||||||
final List<Widget> children;
|
final List<Widget> children;
|
||||||
final List<bool> showIndicators;
|
final List<bool> showIndicators;
|
||||||
|
final bool? showViewCodeButton;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RequestPane> createState() => _RequestPaneState();
|
State<RequestPane> createState() => _RequestPaneState();
|
||||||
@ -29,28 +32,19 @@ class RequestPane extends StatefulWidget {
|
|||||||
|
|
||||||
class _RequestPaneState extends State<RequestPane>
|
class _RequestPaneState extends State<RequestPane>
|
||||||
with TickerProviderStateMixin {
|
with TickerProviderStateMixin {
|
||||||
late final TabController _controller;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_controller = TabController(
|
|
||||||
length: 3,
|
|
||||||
animationDuration: kTabAnimationDuration,
|
|
||||||
vsync: this,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final TabController controller = useTabController(
|
||||||
|
initialLength: 3,
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
if (widget.tabIndex != null) {
|
if (widget.tabIndex != null) {
|
||||||
_controller.index = widget.tabIndex!;
|
controller.index = widget.tabIndex!;
|
||||||
}
|
}
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
context.isMediumWindow
|
(widget.showViewCodeButton ?? !context.isMediumWindow)
|
||||||
? const SizedBox.shrink()
|
? Padding(
|
||||||
: Padding(
|
|
||||||
padding: kP8,
|
padding: kP8,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: kHeaderHeight,
|
height: kHeaderHeight,
|
||||||
@ -76,10 +70,11 @@ class _RequestPaneState extends State<RequestPane>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
TabBar(
|
TabBar(
|
||||||
key: Key(widget.selectedId!),
|
key: Key(widget.selectedId!),
|
||||||
controller: _controller,
|
controller: controller,
|
||||||
overlayColor: kColorTransparentState,
|
overlayColor: kColorTransparentState,
|
||||||
labelPadding: kPh2,
|
labelPadding: kPh2,
|
||||||
onTap: widget.onTapTabBar,
|
onTap: widget.onTapTabBar,
|
||||||
@ -101,7 +96,7 @@ class _RequestPaneState extends State<RequestPane>
|
|||||||
kVSpacer5,
|
kVSpacer5,
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TabBarView(
|
child: TabBarView(
|
||||||
controller: _controller,
|
controller: controller,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
children: widget.children,
|
children: widget.children,
|
||||||
),
|
),
|
||||||
@ -109,10 +104,4 @@ class _RequestPaneState extends State<RequestPane>
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_controller.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -128,6 +128,7 @@ class ResponsePaneHeader extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final bool showClearButton = onClearResponse != null;
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: kPv8,
|
padding: kPv8,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
@ -159,9 +160,11 @@ class ResponsePaneHeader extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
kHSpacer10,
|
kHSpacer10,
|
||||||
ClearResponseButton(
|
showClearButton
|
||||||
onPressed: onClearResponse,
|
? ClearResponseButton(
|
||||||
)
|
onPressed: onClearResponse,
|
||||||
|
)
|
||||||
|
: const SizedBox.shrink(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
101
lib/widgets/table_request.dart
Normal file
101
lib/widgets/table_request.dart
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:data_table_2/data_table_2.dart';
|
||||||
|
import 'package:apidash/widgets/widgets.dart';
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
|
|
||||||
|
class RequestDataTable extends StatelessWidget {
|
||||||
|
const RequestDataTable({
|
||||||
|
super.key,
|
||||||
|
required this.rows,
|
||||||
|
this.keyName,
|
||||||
|
this.valueName,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Map<String, String> rows;
|
||||||
|
final String? keyName;
|
||||||
|
final String? valueName;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final clrScheme = Theme.of(context).colorScheme;
|
||||||
|
|
||||||
|
final List<DataColumn> columns = [
|
||||||
|
DataColumn2(
|
||||||
|
label: Text(keyName ?? kNameField),
|
||||||
|
),
|
||||||
|
const DataColumn2(
|
||||||
|
label: Text('='),
|
||||||
|
fixedWidth: 30,
|
||||||
|
),
|
||||||
|
DataColumn2(
|
||||||
|
label: Text(valueName ?? kNameValue),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
final fieldDecoration = InputDecoration(
|
||||||
|
contentPadding: const EdgeInsets.only(bottom: 12),
|
||||||
|
focusedBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: clrScheme.primary.withOpacity(
|
||||||
|
kHintOpacity,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
enabledBorder: UnderlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: clrScheme.surfaceContainerHighest,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final List<DataRow> dataRows = rows.entries
|
||||||
|
.map<DataRow>(
|
||||||
|
(MapEntry<String, String> entry) => DataRow(
|
||||||
|
cells: <DataCell>[
|
||||||
|
DataCell(
|
||||||
|
ReadOnlyTextField(
|
||||||
|
initialValue: entry.key,
|
||||||
|
decoration: fieldDecoration,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const DataCell(
|
||||||
|
Text('='),
|
||||||
|
),
|
||||||
|
DataCell(
|
||||||
|
ReadOnlyTextField(
|
||||||
|
initialValue: entry.value,
|
||||||
|
decoration: fieldDecoration,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
margin: kP10,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Theme(
|
||||||
|
data: Theme.of(context)
|
||||||
|
.copyWith(scrollbarTheme: kDataTableScrollbarTheme),
|
||||||
|
child: DataTable2(
|
||||||
|
columnSpacing: 12,
|
||||||
|
dividerThickness: 0,
|
||||||
|
horizontalMargin: 0,
|
||||||
|
headingRowHeight: 0,
|
||||||
|
dataRowHeight: kDataTableRowHeight,
|
||||||
|
bottomMargin: kDataTableBottomPadding,
|
||||||
|
isVerticalScrollBarVisible: true,
|
||||||
|
columns: columns,
|
||||||
|
rows: dataRows,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
kVSpacer40,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -32,3 +32,24 @@ class MethodBox extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class StatusCode extends StatelessWidget {
|
||||||
|
const StatusCode({super.key, required this.statusCode, this.style});
|
||||||
|
final int statusCode;
|
||||||
|
final TextStyle? style;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final brightness = Theme.of(context).brightness;
|
||||||
|
final Color color =
|
||||||
|
getResponseStatusCodeColor(statusCode, brightness: brightness);
|
||||||
|
return Text(
|
||||||
|
statusCode.toString(),
|
||||||
|
style: style?.copyWith(color: color) ??
|
||||||
|
Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||||
|
fontFamily: kCodeStyle.fontFamily,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@ export 'button_discord.dart';
|
|||||||
export 'button_repo.dart';
|
export 'button_repo.dart';
|
||||||
export 'button_save_download.dart';
|
export 'button_save_download.dart';
|
||||||
export 'button_send.dart';
|
export 'button_send.dart';
|
||||||
|
export 'card_history_request.dart';
|
||||||
export 'card_request_details.dart';
|
export 'card_request_details.dart';
|
||||||
export 'card_sidebar_environment.dart';
|
export 'card_sidebar_environment.dart';
|
||||||
export 'card_sidebar_history.dart';
|
export 'card_sidebar_history.dart';
|
||||||
@ -28,6 +29,7 @@ export 'field_cell.dart';
|
|||||||
export 'field_header.dart';
|
export 'field_header.dart';
|
||||||
export 'field_json_search.dart';
|
export 'field_json_search.dart';
|
||||||
export 'field_raw.dart';
|
export 'field_raw.dart';
|
||||||
|
export 'field_read_only.dart';
|
||||||
export 'field_url.dart';
|
export 'field_url.dart';
|
||||||
export 'intro_message.dart';
|
export 'intro_message.dart';
|
||||||
export 'json_previewer.dart';
|
export 'json_previewer.dart';
|
||||||
@ -46,9 +48,9 @@ export 'splitview_drawer.dart';
|
|||||||
export 'splitview_dashboard.dart';
|
export 'splitview_dashboard.dart';
|
||||||
export 'splitview_equal.dart';
|
export 'splitview_equal.dart';
|
||||||
export 'splitview_history.dart';
|
export 'splitview_history.dart';
|
||||||
export 'suggestions_menu.dart';
|
|
||||||
export 'tabbar_request_response.dart';
|
export 'tabbar_request_response.dart';
|
||||||
export 'tables.dart';
|
export 'table_map.dart';
|
||||||
|
export 'table_request.dart';
|
||||||
export 'tabs.dart';
|
export 'tabs.dart';
|
||||||
export 'texts.dart';
|
export 'texts.dart';
|
||||||
export 'uint8_audio_player.dart';
|
export 'uint8_audio_player.dart';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
import 'package:apidash/screens/home_page/editor_pane/details_card/code_pane.dart';
|
import 'package:apidash/screens/common_widgets/common_widgets.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import '../models/request_models.dart';
|
import '../models/request_models.dart';
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:apidash/codegen/codegen.dart';
|
import 'package:apidash/codegen/codegen.dart';
|
||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
import 'package:apidash/screens/home_page/editor_pane/details_card/code_pane.dart';
|
import 'package:apidash/screens/common_widgets/common_widgets.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
import '../models/request_models.dart';
|
import '../models/request_models.dart';
|
||||||
|
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:spot/spot.dart';
|
|
||||||
import 'package:apidash/consts.dart';
|
|
||||||
import 'package:apidash/providers/providers.dart';
|
import 'package:apidash/providers/providers.dart';
|
||||||
import 'package:apidash/screens/common_widgets/common_widgets.dart';
|
import 'package:apidash/screens/common_widgets/common_widgets.dart';
|
||||||
import 'package:apidash/screens/dashboard.dart';
|
import 'package:apidash/screens/dashboard.dart';
|
||||||
import 'package:apidash/screens/envvar/environment_page.dart';
|
import 'package:apidash/screens/envvar/environment_page.dart';
|
||||||
import 'package:apidash/screens/home_page/collection_pane.dart';
|
import 'package:apidash/screens/home_page/collection_pane.dart';
|
||||||
import 'package:apidash/screens/home_page/editor_pane/details_card/code_pane.dart';
|
|
||||||
import 'package:apidash/screens/home_page/editor_pane/details_card/response_pane.dart';
|
import 'package:apidash/screens/home_page/editor_pane/details_card/response_pane.dart';
|
||||||
import 'package:apidash/screens/home_page/editor_pane/editor_default.dart';
|
import 'package:apidash/screens/home_page/editor_pane/editor_default.dart';
|
||||||
import 'package:apidash/screens/home_page/editor_pane/editor_pane.dart';
|
import 'package:apidash/screens/home_page/editor_pane/editor_pane.dart';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:apidash/widgets/tables.dart';
|
import 'package:apidash/widgets/table_map.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
Map<String, String> mapInput = {
|
Map<String, String> mapInput = {
|
||||||
|
Reference in New Issue
Block a user