diff --git a/lib/widgets/raw.dart b/lib/widgets/raw.dart new file mode 100644 index 00000000..b3bc6671 --- /dev/null +++ b/lib/widgets/raw.dart @@ -0,0 +1,59 @@ +import 'dart:convert'; + +import 'package:apidash/providers/providers.dart'; +import 'package:apidash/widgets/snackbars.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class Raw extends StatelessWidget { + const Raw({super.key, required this.body, this.style}); + + final String body; + final TextStyle? style; + + String? _formatJson(String data, {required ScaffoldMessengerState sm}) { + try { + final dynamic parsedJson = json.decode(body); + + return const JsonEncoder.withIndent(' ').convert(parsedJson); + } catch (e) { + sm.hideCurrentSnackBar(); + sm.showSnackBar(getSnackBar("Failed to beautify JSON")); + } + return null; + } + + @override + Widget build(BuildContext context) { + var sm = ScaffoldMessenger.of(context); + return Stack( + children: [ + SingleChildScrollView( + child: Consumer(builder: (context, ref, _) { + return SelectableText( + ref.watch(beautifyJsonProvider) ?? body, + style: style, + ); + }), + ), + Align( + alignment: Alignment.topRight, + child: Consumer(builder: (context, ref, _) { + return ref.watch(beautifyJsonProvider) != null + ? const SizedBox.shrink() + : ElevatedButton( + onPressed: () { + ref.read(beautifyJsonProvider.notifier).state = + _formatJson(body, sm: sm); + }, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 10), + ), + child: const Text("Beautify"), + ); + }), + ), + ], + ); + } +} diff --git a/lib/widgets/response_widgets.dart b/lib/widgets/response_widgets.dart index da905749..bd57a867 100644 --- a/lib/widgets/response_widgets.dart +++ b/lib/widgets/response_widgets.dart @@ -1,3 +1,4 @@ +import 'package:apidash/widgets/raw.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:http_parser/http_parser.dart'; @@ -407,22 +408,31 @@ class _BodySuccessState extends State { return Padding( padding: kP10, child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Row( + Wrap( + alignment: WrapAlignment.spaceBetween, children: [ (widget.options == kRawBodyViewOptions) ? const SizedBox() : SegmentedButton( - selectedIcon: Icon( - kResponseBodyViewIcons[currentSeg]![kKeyIcon]), + selectedIcon: showLabel + ? Icon( + kResponseBodyViewIcons[currentSeg]![kKeyIcon], + ) + : null, segments: widget.options .map>( (e) => ButtonSegment( value: e, - label: Text( - kResponseBodyViewIcons[e]![kKeyName]), + label: showLabel + ? Text( + kResponseBodyViewIcons[e]![kKeyName], + ) + : null, icon: Icon( - kResponseBodyViewIcons[e]![kKeyIcon]), + kResponseBodyViewIcons[e]![kKeyIcon], + ), ), ) .toList(), @@ -434,8 +444,11 @@ class _BodySuccessState extends State { }); }, ), - const Spacer(), - kCodeRawBodyViewOptions.contains(currentSeg) + // const Spacer(), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + kCodeRawBodyViewOptions.contains(currentSeg) ? CopyButton( toCopy: widget.body, showLabel: showLabel, @@ -446,6 +459,8 @@ class _BodySuccessState extends State { mimeType: widget.mediaType.mimeType, showLabel: showLabel, ), + ], + ) ], ), kVSpacer10, @@ -491,11 +506,9 @@ class _BodySuccessState extends State { width: double.maxFinite, padding: kP8, decoration: textContainerdecoration, - child: SingleChildScrollView( - child: SelectableText( - widget.body, - style: kCodeStyle, - ), + child: Raw( + body: widget.body, + style: kCodeStyle, ), ), ),