feat: history of requests

This commit is contained in:
DenserMeerkat
2024-07-21 19:55:32 +05:30
parent d9d60961f7
commit 2fc02eadd1
16 changed files with 355 additions and 124 deletions

View File

@ -168,6 +168,7 @@ const kPb15 = EdgeInsets.only(
const kPb70 = EdgeInsets.only(
bottom: 70,
);
const kHSpacer2 = SizedBox(width: 2);
const kHSpacer4 = SizedBox(width: 4);
const kHSpacer5 = SizedBox(width: 5);
const kHSpacer10 = SizedBox(width: 10);
@ -191,8 +192,8 @@ const kRandMax = 100000;
const kSuggestionsMenuWidth = 300.0;
const kSuggestionsMenuMaxHeight = 200.0;
const kReqResTabWidth = 280.0;
const kReqResTabHeight = 32.0;
const kSegmentedTabWidth = 140.0;
const kSegmentedTabHeight = 32.0;
const kDataTableScrollbarTheme = ScrollbarThemeData(
crossAxisMargin: -4,
@ -316,6 +317,20 @@ final kColorHttpMethodPut = Colors.amber.shade900;
final kColorHttpMethodPatch = kColorHttpMethodPut;
final kColorHttpMethodDelete = Colors.red.shade800;
class ButtonData {
ButtonData({
required this.label,
required this.icon,
this.onPressed,
this.tooltip = "",
});
final String label;
final IconData icon;
final VoidCallback? onPressed;
final String tooltip;
}
enum ItemMenuOption {
edit("Rename"),
delete("Delete"),
@ -724,6 +739,8 @@ const kLabelSave = "Save";
const kLabelDownload = "Download";
const kLabelSaving = "Saving";
const kLabelSaved = "Saved";
const kLabelCode = "Code";
const kLabelDuplicate = "Duplicate";
// Request Pane
const kLabelRequest = "Request";
const kLabelHideCode = "Hide Code";

View File

@ -9,6 +9,7 @@ part 'history_meta_model.g.dart';
class HistoryMetaModel with _$HistoryMetaModel {
const factory HistoryMetaModel({
required String historyId,
required String requestId,
@Default("") String name,
required String url,
required HTTPVerb method,

View File

@ -21,6 +21,7 @@ HistoryMetaModel _$HistoryMetaModelFromJson(Map<String, dynamic> json) {
/// @nodoc
mixin _$HistoryMetaModel {
String get historyId => throw _privateConstructorUsedError;
String get requestId => throw _privateConstructorUsedError;
String get name => throw _privateConstructorUsedError;
String get url => throw _privateConstructorUsedError;
HTTPVerb get method => throw _privateConstructorUsedError;
@ -41,6 +42,7 @@ abstract class $HistoryMetaModelCopyWith<$Res> {
@useResult
$Res call(
{String historyId,
String requestId,
String name,
String url,
HTTPVerb method,
@ -62,6 +64,7 @@ class _$HistoryMetaModelCopyWithImpl<$Res, $Val extends HistoryMetaModel>
@override
$Res call({
Object? historyId = null,
Object? requestId = null,
Object? name = null,
Object? url = null,
Object? method = null,
@ -73,6 +76,10 @@ class _$HistoryMetaModelCopyWithImpl<$Res, $Val extends HistoryMetaModel>
? _value.historyId
: historyId // ignore: cast_nullable_to_non_nullable
as String,
requestId: null == requestId
? _value.requestId
: requestId // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
@ -107,6 +114,7 @@ abstract class _$$HistoryMetaModelImplCopyWith<$Res>
@useResult
$Res call(
{String historyId,
String requestId,
String name,
String url,
HTTPVerb method,
@ -126,6 +134,7 @@ class __$$HistoryMetaModelImplCopyWithImpl<$Res>
@override
$Res call({
Object? historyId = null,
Object? requestId = null,
Object? name = null,
Object? url = null,
Object? method = null,
@ -137,6 +146,10 @@ class __$$HistoryMetaModelImplCopyWithImpl<$Res>
? _value.historyId
: historyId // ignore: cast_nullable_to_non_nullable
as String,
requestId: null == requestId
? _value.requestId
: requestId // ignore: cast_nullable_to_non_nullable
as String,
name: null == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
@ -166,6 +179,7 @@ class __$$HistoryMetaModelImplCopyWithImpl<$Res>
class _$HistoryMetaModelImpl implements _HistoryMetaModel {
const _$HistoryMetaModelImpl(
{required this.historyId,
required this.requestId,
this.name = "",
required this.url,
required this.method,
@ -178,6 +192,8 @@ class _$HistoryMetaModelImpl implements _HistoryMetaModel {
@override
final String historyId;
@override
final String requestId;
@override
@JsonKey()
final String name;
@override
@ -191,7 +207,7 @@ class _$HistoryMetaModelImpl implements _HistoryMetaModel {
@override
String toString() {
return 'HistoryMetaModel(historyId: $historyId, name: $name, url: $url, method: $method, responseStatus: $responseStatus, timeStamp: $timeStamp)';
return 'HistoryMetaModel(historyId: $historyId, requestId: $requestId, name: $name, url: $url, method: $method, responseStatus: $responseStatus, timeStamp: $timeStamp)';
}
@override
@ -201,6 +217,8 @@ class _$HistoryMetaModelImpl implements _HistoryMetaModel {
other is _$HistoryMetaModelImpl &&
(identical(other.historyId, historyId) ||
other.historyId == historyId) &&
(identical(other.requestId, requestId) ||
other.requestId == requestId) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.url, url) || other.url == url) &&
(identical(other.method, method) || other.method == method) &&
@ -212,8 +230,8 @@ class _$HistoryMetaModelImpl implements _HistoryMetaModel {
@JsonKey(ignore: true)
@override
int get hashCode => Object.hash(
runtimeType, historyId, name, url, method, responseStatus, timeStamp);
int get hashCode => Object.hash(runtimeType, historyId, requestId, name, url,
method, responseStatus, timeStamp);
@JsonKey(ignore: true)
@override
@ -233,6 +251,7 @@ class _$HistoryMetaModelImpl implements _HistoryMetaModel {
abstract class _HistoryMetaModel implements HistoryMetaModel {
const factory _HistoryMetaModel(
{required final String historyId,
required final String requestId,
final String name,
required final String url,
required final HTTPVerb method,
@ -245,6 +264,8 @@ abstract class _HistoryMetaModel implements HistoryMetaModel {
@override
String get historyId;
@override
String get requestId;
@override
String get name;
@override
String get url;

View File

@ -10,6 +10,7 @@ _$HistoryMetaModelImpl _$$HistoryMetaModelImplFromJson(
Map<String, dynamic> json) =>
_$HistoryMetaModelImpl(
historyId: json['historyId'] as String,
requestId: json['requestId'] as String,
name: json['name'] as String? ?? "",
url: json['url'] as String,
method: $enumDecode(_$HTTPVerbEnumMap, json['method']),
@ -21,6 +22,7 @@ Map<String, dynamic> _$$HistoryMetaModelImplToJson(
_$HistoryMetaModelImpl instance) =>
<String, dynamic>{
'historyId': instance.historyId,
'requestId': instance.requestId,
'name': instance.name,
'url': instance.url,
'method': _$HTTPVerbEnumMap[instance.method]!,

View File

@ -159,6 +159,32 @@ class CollectionStateNotifier
ref.read(hasUnsavedChangesProvider.notifier).state = true;
}
void duplicateFromHistory(HistoryRequestModel historyRequestModel) {
final newId = getNewUuid();
var itemIds = ref.read(requestSequenceProvider);
var currentModel = historyRequestModel;
final newModel = RequestModel(
id: newId,
name: "${currentModel.metaData.name} (history)",
httpRequestModel: currentModel.httpRequestModel,
responseStatus: currentModel.metaData.responseStatus,
message: kResponseCodeReasons[currentModel.metaData.responseStatus],
httpResponseModel: currentModel.httpResponseModel,
isWorking: false,
sendingTime: null,
);
itemIds.insert(0, newId);
var map = {...state!};
map[newId] = newModel;
state = map;
ref.read(requestSequenceProvider.notifier).state = [...itemIds];
ref.read(selectedIdStateProvider.notifier).state = newId;
ref.read(hasUnsavedChangesProvider.notifier).state = true;
}
void update(
String id, {
HTTPVerb? method,
@ -261,6 +287,7 @@ class CollectionStateNotifier
historyId: newHistoryId,
metaData: HistoryMetaModel(
historyId: newHistoryId,
requestId: id,
name: requestModel.name,
url: substitutedHttpRequestModel.url,
method: substitutedHttpRequestModel.method,

View File

@ -27,22 +27,27 @@ class NavbarButton extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final bool isSelected = railIdx == buttonIdx;
final Size size = isCompact ? const Size(56, 32) : const Size(65, 32);
var onPress = isSelected
? null
: () {
if (buttonIdx != null) {
ref.read(navRailIndexStateProvider.notifier).state = buttonIdx!;
if ((railIdx > 2 && buttonIdx! <= 2) ||
!(ref
.read(mobileScaffoldKeyStateProvider)
.currentState
?.isDrawerOpen ??
true)) {
ref.read(leftDrawerStateProvider.notifier).state = false;
}
}
onTap?.call();
};
return MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: isSelected
? null
: () {
if (buttonIdx != null) {
ref.read(navRailIndexStateProvider.notifier).state =
buttonIdx!;
if (railIdx > 2 && buttonIdx! <= 2) {
ref.read(leftDrawerStateProvider.notifier).state = false;
}
}
onTap?.call();
},
onTap: onPress,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@ -56,19 +61,7 @@ class NavbarButton extends ConsumerWidget {
: TextButton.styleFrom(
fixedSize: size,
),
onPressed: isSelected
? null
: () {
if (buttonIdx != null) {
ref.read(navRailIndexStateProvider.notifier).state =
buttonIdx!;
if (railIdx > 2 && buttonIdx! <= 2) {
ref.read(leftDrawerStateProvider.notifier).state =
false;
}
}
onTap?.call();
},
onPressed: onPress,
child: Icon(
isSelected ? selectedIcon : icon,
color: Theme.of(context).colorScheme.onSurfaceVariant,

View File

@ -25,7 +25,7 @@ class _HistoryDetailsState extends ConsumerState<HistoryDetails>
final codePaneVisible = ref.watch(historyCodePaneVisibleStateProvider);
final TabController controller =
useTabController(initialLength: 2, vsync: this);
useTabController(initialLength: 3, vsync: this);
return selectedHistoryRequest != null
? LayoutBuilder(
@ -42,28 +42,45 @@ class _HistoryDetailsState extends ConsumerState<HistoryDetails>
)),
kVSpacer10,
if (isCompact) ...[
RequestResponseTabbar(
SegmentedTabbar(
controller: controller,
tabs: const [
Tab(text: kLabelRequest),
Tab(text: kLabelResponse),
Tab(text: kLabelCode),
],
),
kVSpacer10,
Expanded(
child: TabBarView(
controller: controller,
children: [
HistoryRequestPane(
isCompact: isCompact,
),
const HistoryResponsePane(),
],
))
child: TabBarView(
controller: controller,
children: [
HistoryRequestPane(
isCompact: isCompact,
),
const HistoryResponsePane(),
const CodePane(
isHistoryRequest: true,
),
],
),
),
const HistoryPageBottombar()
] else ...[
Expanded(
child: Padding(
padding: kPh4,
child: RequestDetailsCard(
child: EqualSplitView(
leftWidget: HistoryRequestPane(
isCompact: isCompact,
leftWidget: Column(
children: [
Expanded(
child: HistoryRequestPane(
isCompact: isCompact,
),
),
const HistoryPageBottombar(),
],
),
rightWidget: codePaneVisible
? const CodePane(isHistoryRequest: true)

View File

@ -6,7 +6,6 @@ import 'package:apidash/providers/providers.dart';
import 'package:apidash/utils/utils.dart';
import 'history_pane.dart';
import 'history_viewer.dart';
import 'history_widgets/history_widgets.dart';
class HistoryPage extends ConsumerWidget {
const HistoryPage({
@ -23,14 +22,14 @@ class HistoryPage extends ConsumerWidget {
: 'History';
if (context.isMediumWindow) {
return DrawerSplitView(
scaffoldKey: scaffoldKey,
mainContent: const HistoryViewer(),
title: Text(title),
leftDrawerContent: const HistoryPane(),
actions: const [SizedBox(width: 16)],
onDrawerChanged: (value) =>
ref.read(leftDrawerStateProvider.notifier).state = value,
bottomNavigationBar: const HistoryPageBottombar());
scaffoldKey: scaffoldKey,
mainContent: const HistoryViewer(),
title: Text(title),
leftDrawerContent: const HistoryPane(),
actions: const [SizedBox(width: 16)],
onDrawerChanged: (value) =>
ref.read(leftDrawerStateProvider.notifier).state = value,
);
}
return const Column(
children: [

View File

@ -65,6 +65,7 @@ class HistoryList extends HookConsumerWidget {
child: HistoryExpansionTile(
date: sortedHistoryKeys[index],
requestGroups: requestGroups,
initiallyExpanded: index == 0,
),
);
},
@ -78,10 +79,12 @@ class HistoryExpansionTile extends StatefulHookConsumerWidget {
super.key,
required this.requestGroups,
required this.date,
this.initiallyExpanded = false,
});
final Map<String, List<HistoryMetaModel>> requestGroups;
final DateTime date;
final bool initiallyExpanded;
@override
ConsumerState<HistoryExpansionTile> createState() =>
@ -95,8 +98,9 @@ class _HistoryExpansionTileState extends ConsumerState<HistoryExpansionTile>
final animationController = useAnimationController(
duration: const Duration(milliseconds: 200),
vsync: this,
initialValue: widget.initiallyExpanded ? 1.0 : 0.0,
);
final animation = Tween(begin: 0.25, end: 0.0).animate(animationController);
final animation = Tween(begin: 0.0, end: 0.25).animate(animationController);
final colorScheme = Theme.of(context).colorScheme;
final selectedGroupId = ref.watch(selectedRequestGroupIdStateProvider);
return ExpansionTile(
@ -122,16 +126,16 @@ class _HistoryExpansionTileState extends ConsumerState<HistoryExpansionTile>
),
onExpansionChanged: (value) {
if (value) {
animationController.reverse();
} else {
animationController.forward();
} else {
animationController.reverse();
}
},
trailing: const SizedBox.shrink(),
tilePadding: kPh8,
shape: const RoundedRectangleBorder(),
collapsedBackgroundColor: colorScheme.surfaceContainerLow,
initiallyExpanded: true,
initiallyExpanded: widget.initiallyExpanded,
childrenPadding: kPv8 + kPe4,
children: widget.requestGroups.values.map((item) {
return Padding(

View File

@ -0,0 +1,47 @@
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/models/models.dart';
import 'package:apidash/consts.dart';
class HistoryActionButtons extends ConsumerWidget {
const HistoryActionButtons({super.key, this.historyRequestModel});
final HistoryRequestModel? historyRequestModel;
@override
Widget build(BuildContext context, WidgetRef ref) {
final collectionStateNotifier = ref.watch(collectionStateNotifierProvider);
final isAvailable = collectionStateNotifier?.values.any((element) =>
element.id == historyRequestModel?.metaData.requestId) ??
false;
final requestId = historyRequestModel?.metaData.requestId;
return FilledButtonGroup(buttons: [
ButtonData(
icon: Icons.copy_rounded,
label: kLabelDuplicate,
onPressed: requestId != null
? () {
ref
.read(collectionStateNotifierProvider.notifier)
.duplicateFromHistory(historyRequestModel!);
ref.read(navRailIndexStateProvider.notifier).state = 0;
}
: null,
tooltip: "Duplicate Request",
),
ButtonData(
icon: Icons.north_east_rounded,
label: kLabelRequest,
onPressed: isAvailable && requestId != null
? () {
ref.read(selectedIdStateProvider.notifier).state = requestId;
ref.read(navRailIndexStateProvider.notifier).state = 0;
}
: null,
tooltip: isAvailable ? "Go to Request" : "Couldn't find Request",
),
]);
}
}

View File

@ -1,8 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:apidash/extensions/extensions.dart';
import 'package:apidash/providers/providers.dart';
import 'package:apidash/utils/utils.dart';
import 'package:apidash/consts.dart';
import '../history_requests.dart';
import 'his_action_buttons.dart';
class HistoryPageBottombar extends ConsumerWidget {
const HistoryPageBottombar({
@ -16,59 +19,87 @@ class HistoryPageBottombar extends ConsumerWidget {
final requestGroup = getRequestGroup(
historyMetas?.values.toList(), selectedRequestModel?.metaData);
final requestCount = requestGroup.length;
return Padding(
padding: MediaQuery.of(context).viewInsets,
child: Container(
height: 60 + MediaQuery.paddingOf(context).bottom,
width: MediaQuery.sizeOf(context).width,
padding: EdgeInsets.only(
bottom: MediaQuery.paddingOf(context).bottom,
left: 16,
right: 16,
),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
border: Border(
top: BorderSide(
color: Theme.of(context).colorScheme.onInverseSurface,
width: 1,
),
return Container(
height: 60 + MediaQuery.paddingOf(context).bottom,
width: MediaQuery.sizeOf(context).width,
padding: EdgeInsets.only(
bottom: MediaQuery.paddingOf(context).bottom,
left: 16,
right: 16,
),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
border: Border(
top: BorderSide(
color: Theme.of(context).colorScheme.onInverseSurface,
width: 1,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
requestCount > 1
? Badge(
label: Text(
requestCount > 9 ? '9 +' : requestCount.toString(),
style: const TextStyle(fontWeight: FontWeight.bold),
),
child: IconButton(
style: IconButton.styleFrom(
backgroundColor:
Theme.of(context).colorScheme.secondaryContainer,
),
onPressed: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) {
return ConstrainedBox(
constraints:
const BoxConstraints(maxWidth: 500),
child: const HistorRequestsScrollableSheet());
},
);
},
icon: const Icon(
Icons.keyboard_arrow_up_rounded,
),
),
)
: const SizedBox.shrink(),
],
),
child: context.isMediumWindow
? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
HistoryActionButtons(historyRequestModel: selectedRequestModel),
HistorySheetButton(requestCount: requestCount)
],
)
: Center(
child: HistoryActionButtons(
historyRequestModel: selectedRequestModel)),
);
}
}
class HistorySheetButton extends StatelessWidget {
const HistorySheetButton({
super.key,
required this.requestCount,
});
final int requestCount;
@override
Widget build(BuildContext context) {
final isCompact = context.isCompactWindow;
const icon = Icon(Icons.keyboard_arrow_up_rounded);
return Badge(
isLabelVisible: requestCount > 1,
label: Text(
requestCount > 9 ? '9+' : requestCount.toString(),
style: const TextStyle(fontWeight: FontWeight.bold),
),
child: FilledButton.tonal(
style: FilledButton.styleFrom(
minimumSize: const Size(44, 44),
padding: isCompact ? kP4 : const EdgeInsets.fromLTRB(16, 12, 8, 12),
),
onPressed: requestCount > 1
? () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) {
return ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 500),
child: const HistorRequestsScrollableSheet());
},
);
}
: null,
child: isCompact
? icon
: const Row(
children: [
Text(
"Show All",
style: kTextStyleButton,
),
kHSpacer5,
icon,
],
),
),
);
}

View File

@ -19,13 +19,14 @@ class RequestResponseTabs extends StatelessWidget {
child: EditorPaneRequestURLCard(),
),
kVSpacer10,
RequestResponseTabbar(
SegmentedTabbar(
controller: controller,
tabs: const [
Tab(text: kLabelRequest),
Tab(text: kLabelResponse),
],
),
Expanded(
child: RequestResponseTabviews(
controller: controller,
))
Expanded(child: RequestResponseTabviews(controller: controller))
],
);
}

View File

@ -0,0 +1,60 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
class FilledButtonGroup extends StatelessWidget {
const FilledButtonGroup({super.key, required this.buttons});
final List<ButtonData> buttons;
Widget buildButton(ButtonData buttonData, {bool showLabel = true}) {
final icon = Icon(buttonData.icon, size: 20);
final label = Text(
buttonData.label,
style: kTextStyleButton,
);
return Tooltip(
message: buttonData.tooltip,
child: FilledButton.icon(
style: FilledButton.styleFrom(
minimumSize: const Size(44, 44),
padding: kPh12,
shape: const ContinuousRectangleBorder()),
onPressed: buttonData.onPressed,
label: showLabel
? Row(
children: [
icon,
kHSpacer4,
label,
],
)
: icon,
),
);
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
final showLabel = constraints.maxWidth > buttons.length * 110;
List<Widget> buttonWidgets = buttons
.map((button) => buildButton(button, showLabel: showLabel))
.toList();
List<Widget> buttonsWithSpacers = [];
for (int i = 0; i < buttonWidgets.length; i++) {
buttonsWithSpacers.add(buttonWidgets[i]);
if (i < buttonWidgets.length - 1) {
buttonsWithSpacers.add(kHSpacer2);
}
}
return ClipRRect(
borderRadius: kBorderRadius20,
child: Row(
mainAxisSize: MainAxisSize.min,
children: buttonsWithSpacers,
),
);
});
}
}

View File

@ -1,20 +1,27 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
class RequestResponseTabbar extends StatelessWidget {
const RequestResponseTabbar({
class SegmentedTabbar extends StatelessWidget {
const SegmentedTabbar({
super.key,
required this.controller,
required this.tabs,
this.tabWidth = kSegmentedTabWidth,
this.tabHeight = kSegmentedTabHeight,
});
final TabController controller;
final List<Widget> tabs;
final double tabWidth;
final double tabHeight;
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: kReqResTabWidth,
height: kReqResTabHeight,
margin: kPh4,
width: tabWidth * tabs.length,
height: tabHeight,
decoration: BoxDecoration(
borderRadius: kBorderRadius20,
border: Border.all(
@ -40,14 +47,7 @@ class RequestResponseTabbar extends StatelessWidget {
color: Theme.of(context).colorScheme.primary,
),
controller: controller,
tabs: const <Widget>[
Tab(
text: kLabelRequest,
),
Tab(
text: kLabelResponse,
),
],
tabs: tabs,
),
),
),

View File

@ -20,6 +20,10 @@ class RequestDataTable extends StatelessWidget {
final clrScheme = Theme.of(context).colorScheme;
final List<DataColumn> columns = [
const DataColumn2(
label: Text(''),
fixedWidth: 8,
),
DataColumn2(
label: Text(keyName ?? kNameField),
),
@ -30,6 +34,10 @@ class RequestDataTable extends StatelessWidget {
DataColumn2(
label: Text(valueName ?? kNameValue),
),
const DataColumn2(
label: Text(''),
fixedWidth: 8,
),
];
final fieldDecoration = InputDecoration(
@ -52,6 +60,7 @@ class RequestDataTable extends StatelessWidget {
.map<DataRow>(
(MapEntry<String, String> entry) => DataRow(
cells: <DataCell>[
const DataCell(kHSpacer5),
DataCell(
ReadOnlyTextField(
initialValue: entry.key,
@ -67,6 +76,7 @@ class RequestDataTable extends StatelessWidget {
decoration: fieldDecoration,
),
),
const DataCell(kHSpacer5),
],
),
)

View File

@ -1,6 +1,7 @@
export 'button_clear_response.dart';
export 'button_copy.dart';
export 'button_discord.dart';
export 'button_group_filled.dart';
export 'button_repo.dart';
export 'button_save_download.dart';
export 'button_send.dart';
@ -48,7 +49,7 @@ export 'splitview_drawer.dart';
export 'splitview_dashboard.dart';
export 'splitview_equal.dart';
export 'splitview_history.dart';
export 'tabbar_request_response.dart';
export 'tabbar_segmented.dart';
export 'table_map.dart';
export 'table_request.dart';
export 'tabs.dart';