fix: ui corrections

This commit is contained in:
DenserMeerkat
2024-06-18 17:28:48 +05:30
parent ebe576a118
commit 631b6b2287
15 changed files with 280 additions and 168 deletions

View File

@ -83,6 +83,7 @@ const kPh20v5 = EdgeInsets.symmetric(horizontal: 20, vertical: 5);
const kPh20v10 = EdgeInsets.symmetric(horizontal: 20, vertical: 10); const kPh20v10 = EdgeInsets.symmetric(horizontal: 20, vertical: 10);
const kP10 = EdgeInsets.all(10); const kP10 = EdgeInsets.all(10);
const kPv8 = EdgeInsets.symmetric(vertical: 8); const kPv8 = EdgeInsets.symmetric(vertical: 8);
const kPv6 = EdgeInsets.symmetric(vertical: 6);
const kPv2 = EdgeInsets.symmetric(vertical: 2); const kPv2 = EdgeInsets.symmetric(vertical: 2);
const kPh2 = EdgeInsets.symmetric(horizontal: 2); const kPh2 = EdgeInsets.symmetric(horizontal: 2);
const kPt24o8 = EdgeInsets.only(top: 24, left: 8.0, right: 8.0, bottom: 8.0); const kPt24o8 = EdgeInsets.only(top: 24, left: 8.0, right: 8.0, bottom: 8.0);
@ -118,6 +119,9 @@ const kP8CollectionPane = EdgeInsets.only(
//right: 4.0, //right: 4.0,
// bottom: 8.0, // bottom: 8.0,
); );
const kPt8 = EdgeInsets.only(
top: 8,
);
const kPt28 = EdgeInsets.only( const kPt28 = EdgeInsets.only(
top: 28, top: 28,
); );
@ -137,6 +141,7 @@ const kHSpacer4 = SizedBox(width: 4);
const kHSpacer5 = SizedBox(width: 5); const kHSpacer5 = SizedBox(width: 5);
const kHSpacer10 = SizedBox(width: 10); const kHSpacer10 = SizedBox(width: 10);
const kHSpacer20 = SizedBox(width: 20); const kHSpacer20 = SizedBox(width: 20);
const kHSpacer40 = SizedBox(width: 40);
const kVSpacer5 = SizedBox(height: 5); const kVSpacer5 = SizedBox(height: 5);
const kVSpacer8 = SizedBox(height: 8); const kVSpacer8 = SizedBox(height: 8);
const kVSpacer10 = SizedBox(height: 10); const kVSpacer10 = SizedBox(height: 10);

View File

@ -74,21 +74,21 @@ class EnvironmentDropdown extends ConsumerWidget {
?.removeWhere((element) => element.id == kGlobalEnvironmentId); ?.removeWhere((element) => element.id == kGlobalEnvironmentId);
final activeEnvironment = ref.watch(activeEnvironmentIdStateProvider); final activeEnvironment = ref.watch(activeEnvironmentIdStateProvider);
return Container( return Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all( border: Border.all(
color: Theme.of(context).colorScheme.outlineVariant, color: Theme.of(context).colorScheme.outlineVariant,
),
borderRadius: kBorderRadius8,
), ),
borderRadius: kBorderRadius8, child: EnvironmentPopupMenu(
), activeEnvironment: environments?[activeEnvironment],
child: DropdownButtonEnvironment( environments: environmentsList,
activeEnvironment: environments?[activeEnvironment], onChanged: (value) {
environments: environmentsList, ref.read(activeEnvironmentIdStateProvider.notifier).state =
onChanged: (value) { value?.id;
ref.read(activeEnvironmentIdStateProvider.notifier).state = value?.id; ref.read(hasUnsavedChangesProvider.notifier).state = true;
ref.read(hasUnsavedChangesProvider.notifier).state = true; },
}, ));
),
);
} }
} }
@ -122,7 +122,7 @@ class TitleActionsArray extends StatelessWidget {
borderRadius: kBorderRadius20, borderRadius: kBorderRadius20,
), ),
child: SizedBox( child: SizedBox(
height: 36, height: 32,
child: IntrinsicHeight( child: IntrinsicHeight(
child: Row( child: Row(
children: [ children: [

View File

@ -1,22 +1,23 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:apidash/providers/providers.dart'; import 'package:apidash/providers/providers.dart';
import 'package:apidash/extensions/extensions.dart';
import 'package:apidash/widgets/widgets.dart'; import 'package:apidash/widgets/widgets.dart';
import 'package:apidash/consts.dart'; import 'package:apidash/consts.dart';
class SidebarHeader extends StatelessWidget { class SidebarHeader extends ConsumerWidget {
const SidebarHeader({super.key, this.onAddNew}); const SidebarHeader({super.key, this.onAddNew});
final Function()? onAddNew; final Function()? onAddNew;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context, WidgetRef ref) {
final mobileScaffoldKey = ref.read(mobileScaffoldKeyStateProvider);
return Padding( return Padding(
padding: kPe8, padding: kPe8,
child: Wrap( child: Row(
alignment: WrapAlignment.spaceBetween,
children: [ children: [
const SaveButton(), const SaveButton(),
//const Spacer(), const Spacer(),
ElevatedButton( ElevatedButton(
onPressed: onAddNew, onPressed: onAddNew,
child: const Text( child: const Text(
@ -24,6 +25,17 @@ class SidebarHeader extends StatelessWidget {
style: kTextStyleButton, style: kTextStyleButton,
), ),
), ),
context.isCompactWindow
? IconButton(
style: IconButton.styleFrom(
padding: const EdgeInsets.all(4),
minimumSize: const Size(30, 30)),
onPressed: () {
mobileScaffoldKey.currentState?.closeDrawer();
},
icon: const Icon(Icons.chevron_left),
)
: const SizedBox.shrink(),
], ],
), ),
); );

View File

@ -4,7 +4,7 @@ import 'package:apidash/providers/providers.dart';
import 'package:apidash/extensions/extensions.dart'; import 'package:apidash/extensions/extensions.dart';
import 'package:apidash/consts.dart'; import 'package:apidash/consts.dart';
import '../common/main_editor_widgets.dart'; import '../common/main_editor_widgets.dart';
import 'editor_pane/variables_tabs.dart'; import './editor_pane/variables_pane.dart';
class EnvironmentEditor extends ConsumerWidget { class EnvironmentEditor extends ConsumerWidget {
const EnvironmentEditor({super.key}); const EnvironmentEditor({super.key});
@ -57,28 +57,43 @@ class EnvironmentEditor extends ConsumerWidget {
.removeEnvironment(id!); .removeEnvironment(id!);
}, },
), ),
kHSpacer10,
const EnvironmentDropdown(),
kHSpacer4, kHSpacer4,
], ],
) )
: const SizedBox.shrink(), : const SizedBox.shrink(),
kVSpacer5, kVSpacer5,
Expanded( Expanded(
child: context.isMediumWindow child: Container(
? const VariablesTabs() padding: context.isMediumWindow ? null : kPv6,
: Container( margin: context.isMediumWindow ? null : kP4,
padding: kP6, decoration: context.isMediumWindow
margin: kP4, ? null
decoration: BoxDecoration( : BoxDecoration(
border: Border.all( border: Border.all(
color: Theme.of(context).colorScheme.outlineVariant, color: Theme.of(context).colorScheme.outlineVariant,
width: 1, width: 1,
), ),
borderRadius: kBorderRadius12, borderRadius: kBorderRadius12,
), ),
child: const VariablesTabs(), child: const Column(
children: [
kHSpacer40,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 30),
Text("Variable"),
SizedBox(width: 30),
Text("Value"),
SizedBox(width: 40),
],
), ),
kHSpacer40,
Divider(),
Expanded(child: EditEnvironmentVariables())
],
),
),
), ),
], ],
), ),

View File

@ -49,12 +49,7 @@ class EnvironmentPage extends ConsumerWidget {
}, },
), ),
leftDrawerContent: const EnvironmentsPane(), leftDrawerContent: const EnvironmentsPane(),
actions: const [ actions: const [SizedBox(width: 16)],
Padding(
padding: kPh8,
child: EnvironmentDropdown(),
),
],
onDrawerChanged: (value) => onDrawerChanged: (value) =>
ref.read(leftDrawerStateProvider.notifier).state = value, ref.read(leftDrawerStateProvider.notifier).state = value,
); );

View File

@ -6,7 +6,7 @@ import 'package:apidash/models/environment_model.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 'package:apidash/screens/common/sidebar_widgets.dart'; import '../common/sidebar_widgets.dart';
class EnvironmentsPane extends ConsumerWidget { class EnvironmentsPane extends ConsumerWidget {
const EnvironmentsPane({ const EnvironmentsPane({
@ -70,7 +70,6 @@ class EnvironmentsList extends HookConsumerWidget {
environmentModel: environmentItems[kGlobalEnvironmentId]!, environmentModel: environmentItems[kGlobalEnvironmentId]!,
), ),
), ),
const Divider(endIndent: 4),
Expanded( Expanded(
child: Scrollbar( child: Scrollbar(
controller: scrollController, controller: scrollController,

View File

@ -18,12 +18,7 @@ class RequestEditor extends StatelessWidget {
padding: kPb10, padding: kPb10,
child: Column( child: Column(
children: [ children: [
kVSpacer5, kVSpacer20,
Padding(
padding: kPh8,
child: EditorPaneRequestURLCard(),
),
kVSpacer10,
Expanded( Expanded(
child: EditRequestPane(), child: EditRequestPane(),
), ),

View File

@ -1,4 +1,3 @@
import 'package:apidash/screens/envvar/environment_page.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -8,7 +7,8 @@ import 'package:apidash/providers/providers.dart';
import '../intro_page.dart'; import '../intro_page.dart';
import '../settings_page.dart'; import '../settings_page.dart';
import 'navbar.dart'; import 'navbar.dart';
import 'requests_page.dart'; import 'requests_page/requests_page.dart';
import '../envvar/environment_page.dart';
import 'widgets/page_base.dart'; import 'widgets/page_base.dart';
class MobileDashboard extends ConsumerStatefulWidget { class MobileDashboard extends ConsumerStatefulWidget {
@ -84,7 +84,7 @@ class PageBranch extends ConsumerWidget {
scaffoldBody: SettingsPage(), scaffoldBody: SettingsPage(),
); );
default: default:
return RequestsPage( return RequestResponsePage(
scaffoldKey: scaffoldKey, scaffoldKey: scaffoldKey,
); );
} }

View File

@ -0,0 +1,101 @@
import 'package:flutter/material.dart';
import 'package:apidash/consts.dart';
import '../../home_page/editor_pane/details_card/response_pane.dart';
import '../../home_page/editor_pane/editor_request.dart';
import '../../home_page/editor_pane/url_card.dart';
class RequestResponseTabs extends StatelessWidget {
const RequestResponseTabs({super.key, required this.controller});
final TabController controller;
@override
Widget build(BuildContext context) {
return Column(
children: [
kVSpacer5,
const Padding(
padding: kPh4,
child: EditorPaneRequestURLCard(),
),
kVSpacer10,
RequestResponseTabbar(
controller: controller,
),
Expanded(
child: RequestResponseTabviews(
controller: controller,
))
],
);
}
}
class RequestResponseTabbar extends StatelessWidget {
const RequestResponseTabbar({
super.key,
required this.controller,
});
final TabController controller;
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 280,
height: 32,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(50),
border: Border.all(
color: Theme.of(context).colorScheme.outlineVariant,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(50),
child: TabBar(
dividerColor: Colors.transparent,
indicatorWeight: 0.0,
indicatorSize: TabBarIndicatorSize.tab,
unselectedLabelColor:
Theme.of(context).colorScheme.onSurface.withOpacity(0.4),
labelStyle: kTextStyleTab.copyWith(
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.onPrimary,
),
unselectedLabelStyle: kTextStyleTab,
splashBorderRadius: BorderRadius.circular(50),
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(50),
color: Theme.of(context).colorScheme.primary,
),
controller: controller,
tabs: const <Widget>[
Tab(
text: "Request",
),
Tab(
text: "Response",
),
],
),
),
),
);
}
}
class RequestResponseTabviews extends StatelessWidget {
const RequestResponseTabviews({super.key, required this.controller});
final TabController controller;
@override
Widget build(BuildContext context) {
return TabBarView(controller: controller, children: const [
RequestEditor(),
Padding(
padding: kPt8,
child: ResponsePane(),
),
]);
}
}

View File

@ -1,19 +1,19 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:apidash/providers/providers.dart'; import 'package:apidash/providers/providers.dart';
import 'package:apidash/utils/http_utils.dart'; import 'package:apidash/utils/http_utils.dart';
import 'package:apidash/consts.dart'; import 'package:apidash/consts.dart';
import 'package:apidash/widgets/widgets.dart'; import 'package:apidash/widgets/widgets.dart';
import '../home_page/collection_pane.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../home_page/editor_pane/editor_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/details_card/code_pane.dart';
import '../common/main_editor_widgets.dart'; import '../../common/main_editor_widgets.dart';
import 'response_drawer.dart'; import '../widgets/page_base.dart';
import 'widgets/page_base.dart'; import 'request_response_tabs.dart';
class RequestsPage extends ConsumerWidget { class RequestResponsePage extends StatefulHookConsumerWidget {
const RequestsPage({ const RequestResponsePage({
super.key, super.key,
required this.scaffoldKey, required this.scaffoldKey,
}); });
@ -21,12 +21,21 @@ class RequestsPage extends ConsumerWidget {
final GlobalKey<ScaffoldState> scaffoldKey; final GlobalKey<ScaffoldState> scaffoldKey;
@override @override
Widget build(BuildContext context, WidgetRef ref) { ConsumerState<RequestResponsePage> createState() =>
_RequestResponsePageState();
}
class _RequestResponsePageState extends ConsumerState<RequestResponsePage>
with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
final id = ref.watch(selectedIdStateProvider); final id = ref.watch(selectedIdStateProvider);
final name = getRequestTitleFromUrl( final name = getRequestTitleFromUrl(
ref.watch(selectedRequestModelProvider.select((value) => value?.name))); ref.watch(selectedRequestModelProvider.select((value) => value?.name)));
final TabController requestResponseTabController =
useTabController(initialLength: 2, vsync: this);
return TwoDrawerScaffold( return TwoDrawerScaffold(
scaffoldKey: scaffoldKey, scaffoldKey: widget.scaffoldKey,
title: ScaffoldTitle( title: ScaffoldTitle(
title: name, title: name,
onSelected: (ItemMenuOption item) { onSelected: (ItemMenuOption item) {
@ -46,19 +55,25 @@ class RequestsPage extends ConsumerWidget {
}, },
), ),
leftDrawerContent: const CollectionPane(), leftDrawerContent: const CollectionPane(),
rightDrawerContent: const ResponseDrawer(), actions: const [Padding(padding: kPh8, child: EnvironmentDropdown())],
mainContent: const RequestEditorPane(), mainContent: RequestResponseTabs(
bottomNavigationBar: const RequestPageBottombar(), controller: requestResponseTabController,
),
bottomNavigationBar: RequestResponsePageBottombar(
requestResponseTabController: requestResponseTabController,
),
onDrawerChanged: (value) => onDrawerChanged: (value) =>
ref.read(leftDrawerStateProvider.notifier).state = value, ref.read(leftDrawerStateProvider.notifier).state = value,
); );
} }
} }
class RequestPageBottombar extends ConsumerWidget { class RequestResponsePageBottombar extends ConsumerWidget {
const RequestPageBottombar({ const RequestResponsePageBottombar({
super.key, super.key,
required this.requestResponseTabController,
}); });
final TabController requestResponseTabController;
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
@ -104,10 +119,9 @@ class RequestPageBottombar extends ConsumerWidget {
), ),
SendButton( SendButton(
onTap: () { onTap: () {
ref if (requestResponseTabController.index != 1) {
.read(mobileScaffoldKeyStateProvider) requestResponseTabController.animateTo(1);
.currentState! }
.openEndDrawer();
}, },
), ),
], ],

View File

@ -1,33 +0,0 @@
import 'package:flutter/material.dart';
import '../home_page/editor_pane/details_card/response_pane.dart';
class ResponseDrawer extends StatelessWidget {
const ResponseDrawer({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.surface,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(8)),
),
leading: IconButton(
icon: const Icon(Icons.arrow_back_rounded),
onPressed: () {
Navigator.of(context).pop();
},
),
scrolledUnderElevation: 0,
centerTitle: true,
title: const Text("Response"),
),
body: Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.paddingOf(context).bottom,
),
child: const ResponsePane(),
),
);
}
}

View File

@ -190,7 +190,8 @@ class SidebarEnvironmentCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme; final colorScheme = Theme.of(context).colorScheme;
final Color color = colorScheme.surface; final Color color =
isGlobal ? colorScheme.secondaryContainer : colorScheme.surface;
final Color colorVariant = colorScheme.surfaceVariant.withOpacity(0.5); final Color colorVariant = colorScheme.surfaceVariant.withOpacity(0.5);
final Color surfaceTint = colorScheme.primary; final Color surfaceTint = colorScheme.primary;
bool isSelected = selectedId == id; bool isSelected = selectedId == id;
@ -206,7 +207,7 @@ class SidebarEnvironmentCard extends StatelessWidget {
), ),
elevation: isSelected ? 1 : 0, elevation: isSelected ? 1 : 0,
surfaceTintColor: isSelected ? surfaceTint : null, surfaceTintColor: isSelected ? surfaceTint : null,
color: isSelected color: isSelected && !isGlobal
? colorScheme.brightness == Brightness.dark ? colorScheme.brightness == Brightness.dark
? colorVariant ? colorVariant
: color : color

View File

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:apidash/utils/utils.dart'; import 'package:apidash/utils/utils.dart';
import 'package:apidash/consts.dart'; import 'package:apidash/consts.dart';
import 'package:apidash/extensions/extensions.dart'; import 'package:apidash/extensions/extensions.dart';
import 'package:apidash/models/models.dart';
class DropdownButtonHttpMethod extends StatelessWidget { class DropdownButtonHttpMethod extends StatelessWidget {
const DropdownButtonHttpMethod({ const DropdownButtonHttpMethod({
@ -188,67 +187,3 @@ class DropdownButtonCodegenLanguage extends StatelessWidget {
); );
} }
} }
class DropdownButtonEnvironment extends StatelessWidget {
const DropdownButtonEnvironment({
super.key,
this.activeEnvironment,
this.onChanged,
this.environments,
});
final EnvironmentModel? activeEnvironment;
final void Function(EnvironmentModel? value)? onChanged;
final List<EnvironmentModel>? environments;
final EnvironmentModel? noneEnvironmentModel = null;
@override
Widget build(BuildContext context) {
final surfaceColor = Theme.of(context).colorScheme.surface;
final characterLimit = context.isCompactWindow ? 12 : 15;
return DropdownButton<EnvironmentModel>(
isDense: true,
padding: kPs0o6,
focusColor: surfaceColor,
value: activeEnvironment,
icon: const Icon(Icons.unfold_more_rounded),
elevation: 4,
underline: Container(
height: 0,
),
borderRadius: kBorderRadius8,
onChanged: onChanged,
items: [
DropdownMenuItem<EnvironmentModel>(
value: noneEnvironmentModel,
child: Padding(
padding: EdgeInsets.only(left: context.isMediumWindow ? 8 : 16),
child: Text(
"No Environment",
style: kTextStyleButtonSmall.copyWith(
fontWeight: FontWeight.bold,
),
),
),
),
...environments?.map<DropdownMenuItem<EnvironmentModel>>(
(EnvironmentModel environmentModel) {
final name = getEnvironmentTitle(environmentModel.name);
return DropdownMenuItem<EnvironmentModel>(
value: environmentModel,
child: Padding(
padding:
EdgeInsets.only(left: context.isMediumWindow ? 8 : 16),
child: Text(
name.clip(characterLimit),
style: kTextStyleButtonSmall.copyWith(
fontWeight: FontWeight.bold,
),
),
),
);
}).toList() ??
[]
],
);
}
}

View File

@ -0,0 +1,72 @@
import 'package:apidash/consts.dart';
import 'package:apidash/extensions/extensions.dart';
import 'package:flutter/material.dart';
import 'package:apidash/models/models.dart';
import 'package:apidash/utils/utils.dart';
class EnvironmentPopupMenu extends StatelessWidget {
const EnvironmentPopupMenu({
super.key,
this.activeEnvironment,
this.onChanged,
this.environments,
});
final EnvironmentModel? activeEnvironment;
final void Function(EnvironmentModel? value)? onChanged;
final List<EnvironmentModel>? environments;
final EnvironmentModel? noneEnvironmentModel = null;
@override
Widget build(BuildContext context) {
final activeEnvironmentName = getEnvironmentTitle(activeEnvironment?.name);
final textClipLength = context.isCompactWindow ? 6 : 10;
final double boxLength = context.isCompactWindow ? 100 : 130;
return PopupMenuButton(
tooltip: "Select Environment",
surfaceTintColor: kColorTransparent,
constraints: BoxConstraints(minWidth: boxLength),
itemBuilder: (BuildContext context) {
return [
PopupMenuItem(
value: noneEnvironmentModel,
onTap: () {
onChanged?.call(null);
},
child: const Text("None"),
),
...environments!.map((EnvironmentModel environment) {
final name = getEnvironmentTitle(environment.name);
return PopupMenuItem(
value: environment,
child: Text(
name,
softWrap: false,
overflow: TextOverflow.ellipsis,
),
);
})
];
},
onSelected: onChanged,
child: Container(
width: boxLength,
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
activeEnvironment == null
? "None"
: activeEnvironmentName.clip(textClipLength),
softWrap: false,
),
const Icon(
Icons.unfold_more,
size: 16,
)
],
),
),
);
}
}

View File

@ -13,6 +13,7 @@ export 'json_previewer.dart';
export 'markdown.dart'; export 'markdown.dart';
export 'menus.dart'; export 'menus.dart';
export 'overlay_widget.dart'; export 'overlay_widget.dart';
export 'popup_menus.dart';
export 'previewer.dart'; export 'previewer.dart';
export 'request_widgets.dart'; export 'request_widgets.dart';
export 'response_widgets.dart'; export 'response_widgets.dart';