mirror of
https://github.com/foss42/apidash.git
synced 2025-12-01 10:17:47 +08:00
Refactored collection state management to handle API type changes and AI request models. Updated widgets and tests to support nullable HTTP methods and AI request models, and improved response body rendering for AI responses.
189 lines
7.1 KiB
Dart
189 lines
7.1 KiB
Dart
import 'package:apidash_core/apidash_core.dart';
|
|
import 'package:apidash_design_system/apidash_design_system.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:apidash/utils/utils.dart';
|
|
import 'package:apidash/widgets/widgets.dart';
|
|
import 'package:apidash/consts.dart';
|
|
import 'button_share.dart';
|
|
|
|
class ResponseBodySuccess extends StatefulWidget {
|
|
const ResponseBodySuccess({
|
|
super.key,
|
|
required this.mediaType,
|
|
required this.body,
|
|
required this.options,
|
|
required this.bytes,
|
|
this.formattedBody,
|
|
this.highlightLanguage,
|
|
this.sseOutput,
|
|
this.isAIResponse = false,
|
|
this.aiRequestModel,
|
|
});
|
|
final MediaType mediaType;
|
|
final List<ResponseBodyView> options;
|
|
final String body;
|
|
final Uint8List bytes;
|
|
final String? formattedBody;
|
|
final List<String>? sseOutput;
|
|
final String? highlightLanguage;
|
|
final bool isAIResponse;
|
|
final AIRequestModel? aiRequestModel;
|
|
|
|
@override
|
|
State<ResponseBodySuccess> createState() => _ResponseBodySuccessState();
|
|
}
|
|
|
|
class _ResponseBodySuccessState extends State<ResponseBodySuccess> {
|
|
int segmentIdx = 0;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
var currentSeg = widget.options[segmentIdx];
|
|
var codeTheme = Theme.of(context).brightness == Brightness.light
|
|
? kLightCodeTheme
|
|
: kDarkCodeTheme;
|
|
final textContainerdecoration = BoxDecoration(
|
|
color: Theme.of(context).colorScheme.surfaceContainerLow,
|
|
border: Border.all(
|
|
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
|
),
|
|
borderRadius: kBorderRadius8,
|
|
);
|
|
|
|
return LayoutBuilder(
|
|
builder: (BuildContext context, BoxConstraints constraints) {
|
|
var showLabel = showButtonLabelsInBodySuccess(
|
|
widget.options.length,
|
|
constraints.maxWidth,
|
|
);
|
|
return Padding(
|
|
padding: kP10,
|
|
child: Column(
|
|
children: [
|
|
Row(
|
|
children: [
|
|
(widget.options == kRawBodyViewOptions)
|
|
? const SizedBox()
|
|
: SegmentedButton<ResponseBodyView>(
|
|
style: SegmentedButton.styleFrom(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
|
),
|
|
selectedIcon: Icon(currentSeg.icon),
|
|
segments: widget.options
|
|
.map<ButtonSegment<ResponseBodyView>>(
|
|
(e) => ButtonSegment<ResponseBodyView>(
|
|
value: e,
|
|
label: Text(e.label),
|
|
icon: constraints.maxWidth >
|
|
kMinWindowSize.width
|
|
? Icon(e.icon)
|
|
: null,
|
|
),
|
|
)
|
|
.toList(),
|
|
selected: {currentSeg},
|
|
onSelectionChanged: (newSelection) {
|
|
setState(() {
|
|
segmentIdx =
|
|
widget.options.indexOf(newSelection.first);
|
|
});
|
|
},
|
|
),
|
|
const Spacer(),
|
|
((widget.options == kPreviewRawBodyViewOptions) ||
|
|
kCodeRawBodyViewOptions.contains(currentSeg))
|
|
? CopyButton(
|
|
toCopy: widget.formattedBody ?? widget.body,
|
|
showLabel: showLabel,
|
|
)
|
|
: const SizedBox(),
|
|
kIsMobile
|
|
? ShareButton(
|
|
toShare: widget.formattedBody ?? widget.body,
|
|
showLabel: showLabel,
|
|
)
|
|
: SaveInDownloadsButton(
|
|
content: widget.bytes,
|
|
mimeType: widget.mediaType.mimeType,
|
|
showLabel: showLabel,
|
|
),
|
|
],
|
|
),
|
|
kVSpacer10,
|
|
switch (currentSeg) {
|
|
ResponseBodyView.preview || ResponseBodyView.none => Expanded(
|
|
child: Container(
|
|
width: double.maxFinite,
|
|
padding: kP8,
|
|
decoration: textContainerdecoration,
|
|
child: Previewer(
|
|
bytes: widget.bytes,
|
|
body: widget.body,
|
|
type: widget.mediaType.type,
|
|
subtype: widget.mediaType.subtype,
|
|
hasRaw: widget.options.contains(ResponseBodyView.raw),
|
|
),
|
|
),
|
|
),
|
|
ResponseBodyView.code => Expanded(
|
|
child: Container(
|
|
width: double.maxFinite,
|
|
padding: kP8,
|
|
decoration: textContainerdecoration,
|
|
child: CodePreviewer(
|
|
code: widget.formattedBody ?? widget.body,
|
|
theme: codeTheme,
|
|
language: widget.highlightLanguage,
|
|
textStyle: kCodeStyle,
|
|
),
|
|
),
|
|
),
|
|
ResponseBodyView.answer => Expanded(
|
|
child: Container(
|
|
width: double.maxFinite,
|
|
padding: kP8,
|
|
decoration: textContainerdecoration,
|
|
child: SingleChildScrollView(
|
|
child: SelectableText(
|
|
widget.formattedBody ?? widget.body,
|
|
style: kCodeStyle,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
ResponseBodyView.raw => Expanded(
|
|
child: Container(
|
|
width: double.maxFinite,
|
|
padding: kP8,
|
|
decoration: textContainerdecoration,
|
|
child: SingleChildScrollView(
|
|
child: SelectableText(
|
|
widget.isAIResponse
|
|
? widget.body
|
|
: (widget.formattedBody ?? widget.body),
|
|
style: kCodeStyle,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
ResponseBodyView.sse => Expanded(
|
|
child: Container(
|
|
width: double.maxFinite,
|
|
padding: kP8,
|
|
decoration: textContainerdecoration,
|
|
child: SSEDisplay(
|
|
sseOutput: widget.sseOutput,
|
|
aiRequestModel: widget.aiRequestModel,
|
|
),
|
|
),
|
|
),
|
|
}
|
|
],
|
|
),
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|