mirror of
https://github.com/foss42/apidash.git
synced 2025-08-06 13:51:20 +08:00
response pane widgets
This commit is contained in:
@ -1,217 +1,74 @@
|
|||||||
import 'dart:convert';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_json_view/flutter_json_view.dart';
|
|
||||||
import 'package:apidash/providers/providers.dart';
|
import 'package:apidash/providers/providers.dart';
|
||||||
import 'package:apidash/models/models.dart';
|
|
||||||
import 'package:apidash/utils/utils.dart';
|
import 'package:apidash/utils/utils.dart';
|
||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
|
import 'response_tabs/response_tabs.dart';
|
||||||
|
|
||||||
final jsonViewTheme = JsonViewTheme(
|
class ResponseDetails extends ConsumerStatefulWidget {
|
||||||
defaultTextStyle: codeStyle,
|
const ResponseDetails({super.key});
|
||||||
viewType: JsonViewType.collapsible,
|
|
||||||
backgroundColor: colorBg,
|
|
||||||
stringStyle: const TextStyle(color: Colors.brown),
|
|
||||||
closeIcon: const Icon(
|
|
||||||
Icons.arrow_drop_up,
|
|
||||||
size: 18,
|
|
||||||
),
|
|
||||||
openIcon: const Icon(
|
|
||||||
Icons.arrow_drop_down,
|
|
||||||
size: 18,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
class ResponseViewer extends ConsumerStatefulWidget {
|
|
||||||
const ResponseViewer({super.key});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<ResponseViewer> createState() => _ResponseViewerState();
|
ConsumerState<ResponseDetails> createState() => _ResponseDetailsState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ResponseViewerState extends ConsumerState<ResponseViewer> {
|
class _ResponseDetailsState extends ConsumerState<ResponseDetails> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final activeId = ref.watch(activeIdStateProvider);
|
final activeId = ref.watch(activeIdStateProvider);
|
||||||
final sentRequestId = ref.watch(sentRequestIdStateProvider);
|
final collection = ref.read(collectionStateNotifierProvider);
|
||||||
final collection = ref.watch(collectionStateNotifierProvider);
|
|
||||||
final idIdx = collection.indexWhere((m) => m.id == activeId);
|
final idIdx = collection.indexWhere((m) => m.id == activeId);
|
||||||
final responseStatus = collection[idIdx].responseStatus;
|
final responseStatus = collection[idIdx].responseStatus;
|
||||||
final message = collection[idIdx].message;
|
final message = collection[idIdx].message;
|
||||||
return Container();
|
final responseModel = collection[idIdx].responseModel;
|
||||||
}
|
return Column(
|
||||||
}
|
children: [
|
||||||
|
Row(
|
||||||
class ResponseiViewer extends StatelessWidget {
|
children: const [
|
||||||
final int statusCode;
|
Text(
|
||||||
final String? message;
|
"Response",
|
||||||
final ResponseModel responseModel;
|
style: textStyleButton,
|
||||||
|
|
||||||
const ResponseiViewer({
|
|
||||||
super.key,
|
|
||||||
required this.statusCode,
|
|
||||||
required this.message,
|
|
||||||
required this.responseModel,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
var requestHeaders = responseModel.requestHeaders ?? {};
|
|
||||||
var responseHeaders = responseModel.headers ?? {};
|
|
||||||
var body = responseModel.body ?? '';
|
|
||||||
return Padding(
|
|
||||||
padding: p10,
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: const [
|
|
||||||
Text(
|
|
||||||
"Response",
|
|
||||||
style: textStyleButton,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 5),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
width: 50,
|
|
||||||
child: Text(
|
|
||||||
statusCode.toString(),
|
|
||||||
style: codeStyle.copyWith(
|
|
||||||
color: getResponseStatusCodeColor(statusCode),
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
message ?? "",
|
|
||||||
style: codeStyle.copyWith(
|
|
||||||
color: getResponseStatusCodeColor(statusCode),
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(
|
|
||||||
width: 100,
|
|
||||||
child: Text(
|
|
||||||
humanizeDuration(responseModel.time),
|
|
||||||
style: codeStyle.copyWith(
|
|
||||||
color: getResponseStatusCodeColor(statusCode),
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 5),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
"Request Headers (${requestHeaders.length} items)",
|
|
||||||
style: codeStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (requestHeaders.isNotEmpty)
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await Clipboard.setData(
|
|
||||||
ClipboardData(text: json.encode(requestHeaders)));
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
children: const [
|
|
||||||
Icon(
|
|
||||||
Icons.content_copy,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
Text("Copy")
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (requestHeaders.isNotEmpty) const SizedBox(height: 5),
|
|
||||||
if (requestHeaders.isNotEmpty)
|
|
||||||
JsonView.map(
|
|
||||||
requestHeaders,
|
|
||||||
theme: jsonViewTheme,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 5),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
"Response Headers (${responseHeaders.length} items)",
|
|
||||||
style: codeStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (responseHeaders.isNotEmpty)
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await Clipboard.setData(
|
|
||||||
ClipboardData(text: json.encode(responseHeaders)));
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
children: const [
|
|
||||||
Icon(
|
|
||||||
Icons.content_copy,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
Text("Copy")
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
if (responseHeaders.isNotEmpty) const SizedBox(height: 5),
|
|
||||||
if (responseHeaders.isNotEmpty)
|
|
||||||
JsonView.map(
|
|
||||||
responseHeaders,
|
|
||||||
theme: jsonViewTheme,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 5),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
"Body ${body.isEmpty ? '(empty)' : ''}",
|
|
||||||
style: codeStyle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (body.isNotEmpty)
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await Clipboard.setData(ClipboardData(text: body));
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
children: const [
|
|
||||||
Icon(
|
|
||||||
Icons.content_copy,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
Text("Copy")
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 5),
|
|
||||||
if (body.isNotEmpty &&
|
|
||||||
responseModel.contentType!.startsWith(JSON_MIMETYPE))
|
|
||||||
JsonView.string(
|
|
||||||
body,
|
|
||||||
theme: jsonViewTheme,
|
|
||||||
),
|
|
||||||
if (body.isNotEmpty &&
|
|
||||||
responseModel.contentType!.startsWith("text/"))
|
|
||||||
Text(body),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 5),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 50,
|
||||||
|
child: Text(
|
||||||
|
"$responseStatus",
|
||||||
|
style: codeStyle.copyWith(
|
||||||
|
color: getResponseStatusCodeColor(responseStatus),
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
message ?? "",
|
||||||
|
style: codeStyle.copyWith(
|
||||||
|
color: getResponseStatusCodeColor(responseStatus),
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
width: 100,
|
||||||
|
child: Text(
|
||||||
|
humanizeDuration(responseModel!.time),
|
||||||
|
style: codeStyle.copyWith(
|
||||||
|
color: getResponseStatusCodeColor(responseStatus),
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Expanded(
|
||||||
|
child: ResponseTabs(),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ class _ResponsePaneState extends ConsumerState<ResponsePane> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final activeId = ref.watch(activeIdStateProvider);
|
final activeId = ref.watch(activeIdStateProvider);
|
||||||
final sentRequestId = ref.watch(sentRequestIdStateProvider);
|
final sentRequestId = ref.watch(sentRequestIdStateProvider);
|
||||||
final collection = ref.watch(collectionStateNotifierProvider);
|
final collection = ref.read(collectionStateNotifierProvider);
|
||||||
final idIdx = collection.indexWhere((m) => m.id == activeId);
|
final idIdx = collection.indexWhere((m) => m.id == activeId);
|
||||||
final responseStatus = collection[idIdx].responseStatus;
|
final responseStatus = collection[idIdx].responseStatus;
|
||||||
final message = collection[idIdx].message;
|
final message = collection[idIdx].message;
|
||||||
@ -33,9 +33,8 @@ class _ResponsePaneState extends ConsumerState<ResponsePane> {
|
|||||||
}
|
}
|
||||||
if (responseStatus == -1) {
|
if (responseStatus == -1) {
|
||||||
return ErrorMessage(message: message);
|
return ErrorMessage(message: message);
|
||||||
} else {
|
|
||||||
return const ResponseViewer();
|
|
||||||
}
|
}
|
||||||
|
return const ResponseDetails();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter_json_view/flutter_json_view.dart';
|
||||||
|
import 'package:apidash/providers/providers.dart';
|
||||||
|
import 'package:apidash/widgets/jsonview.dart';
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
|
|
||||||
|
class ResponseBody extends ConsumerStatefulWidget {
|
||||||
|
const ResponseBody({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<ResponseBody> createState() => _ResponseBodyState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ResponseBodyState extends ConsumerState<ResponseBody> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final activeId = ref.watch(activeIdStateProvider);
|
||||||
|
final collection = ref.watch(collectionStateNotifierProvider);
|
||||||
|
final idIdx = collection.indexWhere((m) => m.id == activeId);
|
||||||
|
final responseModel = collection[idIdx].responseModel;
|
||||||
|
var body = responseModel?.body ?? '';
|
||||||
|
var contentType = responseModel?.contentType ?? "";
|
||||||
|
return Padding(
|
||||||
|
padding: p10,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
"Body ${body.isEmpty ? '(empty)' : ''}",
|
||||||
|
style: codeStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (body.isNotEmpty)
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await Clipboard.setData(ClipboardData(text: body));
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: const [
|
||||||
|
Icon(
|
||||||
|
Icons.content_copy,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
Text("Copy")
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
if (body.isNotEmpty && contentType.startsWith(JSON_MIMETYPE))
|
||||||
|
JsonView.string(
|
||||||
|
body,
|
||||||
|
theme: jsonViewTheme,
|
||||||
|
),
|
||||||
|
if (body.isNotEmpty && contentType.startsWith("text/")) Text(body),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter_json_view/flutter_json_view.dart';
|
||||||
|
import 'package:apidash/providers/providers.dart';
|
||||||
|
import 'package:apidash/widgets/jsonview.dart';
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
|
|
||||||
|
class ResponseHeaders extends ConsumerStatefulWidget {
|
||||||
|
const ResponseHeaders({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<ResponseHeaders> createState() => _ResponseHeadersState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ResponseHeadersState extends ConsumerState<ResponseHeaders> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final activeId = ref.watch(activeIdStateProvider);
|
||||||
|
final collection = ref.watch(collectionStateNotifierProvider);
|
||||||
|
final idIdx = collection.indexWhere((m) => m.id == activeId);
|
||||||
|
final requestHeaders =
|
||||||
|
collection[idIdx].responseModel?.requestHeaders ?? {};
|
||||||
|
final responseHeaders = collection[idIdx].responseModel?.headers ?? {};
|
||||||
|
return Container(
|
||||||
|
decoration: tableContainerDecoration,
|
||||||
|
margin: p5,
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
"Request Headers (${requestHeaders.length} items)",
|
||||||
|
style: codeStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (requestHeaders.isNotEmpty)
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await Clipboard.setData(
|
||||||
|
ClipboardData(text: json.encode(requestHeaders)));
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: const [
|
||||||
|
Icon(
|
||||||
|
Icons.content_copy,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
Text("Copy")
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (requestHeaders.isNotEmpty) const SizedBox(height: 5),
|
||||||
|
if (requestHeaders.isNotEmpty)
|
||||||
|
JsonView.map(
|
||||||
|
requestHeaders,
|
||||||
|
theme: jsonViewTheme,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
"Response Headers (${responseHeaders.length} items)",
|
||||||
|
style: codeStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (responseHeaders.isNotEmpty)
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
await Clipboard.setData(
|
||||||
|
ClipboardData(text: json.encode(responseHeaders)));
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: const [
|
||||||
|
Icon(
|
||||||
|
Icons.content_copy,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
Text("Copy")
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (responseHeaders.isNotEmpty) const SizedBox(height: 5),
|
||||||
|
if (responseHeaders.isNotEmpty)
|
||||||
|
JsonView.map(
|
||||||
|
responseHeaders,
|
||||||
|
theme: jsonViewTheme,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:apidash/providers/providers.dart';
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
|
import 'response_headers.dart';
|
||||||
|
import 'response_body.dart';
|
||||||
|
|
||||||
|
class ResponseTabs extends ConsumerStatefulWidget {
|
||||||
|
const ResponseTabs({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
ConsumerState<ResponseTabs> createState() => _ResponseTabsState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ResponseTabsState extends ConsumerState<ResponseTabs>
|
||||||
|
with TickerProviderStateMixin {
|
||||||
|
late final TabController _controller;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller = TabController(
|
||||||
|
length: 2,
|
||||||
|
animationDuration: tabAnimationDuration,
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final activeId = ref.watch(activeIdStateProvider);
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
TabBar(
|
||||||
|
key: Key(activeId!),
|
||||||
|
controller: _controller,
|
||||||
|
overlayColor: colorTransparent,
|
||||||
|
onTap: (index) {},
|
||||||
|
tabs: const [
|
||||||
|
SizedBox(
|
||||||
|
height: 45,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'Body',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
|
softWrap: false,
|
||||||
|
style: textStyleButton,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 45,
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'Headers',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
|
style: textStyleButton,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TabBarView(
|
||||||
|
controller: _controller,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
children: const [
|
||||||
|
ResponseBody(),
|
||||||
|
ResponseHeaders(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
18
lib/widgets/jsonview.dart
Normal file
18
lib/widgets/jsonview.dart
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import 'package:flutter_json_view/flutter_json_view.dart';
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
final jsonViewTheme = JsonViewTheme(
|
||||||
|
defaultTextStyle: codeStyle,
|
||||||
|
viewType: JsonViewType.collapsible,
|
||||||
|
backgroundColor: colorBg,
|
||||||
|
stringStyle: const TextStyle(color: Colors.brown),
|
||||||
|
closeIcon: const Icon(
|
||||||
|
Icons.arrow_drop_up,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
openIcon: const Icon(
|
||||||
|
Icons.arrow_drop_down,
|
||||||
|
size: 18,
|
||||||
|
),
|
||||||
|
);
|
Reference in New Issue
Block a user