mirror of
https://github.com/foss42/apidash.git
synced 2025-05-21 00:09:55 +08:00
feat: environment pane
This commit is contained in:
@ -265,7 +265,7 @@ final kColorHttpMethodPut = Colors.amber.shade900;
|
|||||||
final kColorHttpMethodPatch = kColorHttpMethodPut;
|
final kColorHttpMethodPatch = kColorHttpMethodPut;
|
||||||
final kColorHttpMethodDelete = Colors.red.shade800;
|
final kColorHttpMethodDelete = Colors.red.shade800;
|
||||||
|
|
||||||
enum RequestItemMenuOption { edit, delete, duplicate }
|
enum ItemMenuOption { edit, delete, duplicate }
|
||||||
|
|
||||||
enum HTTPVerb { get, head, post, put, patch, delete }
|
enum HTTPVerb { get, head, post, put, patch, delete }
|
||||||
|
|
||||||
|
@ -7,9 +7,13 @@ part 'environment_model.g.dart';
|
|||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
class EnvironmentModel with _$EnvironmentModel {
|
class EnvironmentModel with _$EnvironmentModel {
|
||||||
|
@JsonSerializable(
|
||||||
|
explicitToJson: true,
|
||||||
|
anyMap: true,
|
||||||
|
)
|
||||||
const factory EnvironmentModel({
|
const factory EnvironmentModel({
|
||||||
required String id,
|
required String id,
|
||||||
@Default("New Environment") String name,
|
@Default("") String name,
|
||||||
@Default([]) List<EnvironmentVariableModel> values,
|
@Default([]) List<EnvironmentVariableModel> values,
|
||||||
}) = _EnvironmentModel;
|
}) = _EnvironmentModel;
|
||||||
|
|
||||||
@ -19,6 +23,10 @@ class EnvironmentModel with _$EnvironmentModel {
|
|||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
class EnvironmentVariableModel with _$EnvironmentVariableModel {
|
class EnvironmentVariableModel with _$EnvironmentVariableModel {
|
||||||
|
@JsonSerializable(
|
||||||
|
explicitToJson: true,
|
||||||
|
anyMap: true,
|
||||||
|
)
|
||||||
const factory EnvironmentVariableModel({
|
const factory EnvironmentVariableModel({
|
||||||
required String key,
|
required String key,
|
||||||
required String value,
|
required String value,
|
||||||
|
@ -118,11 +118,12 @@ class __$$EnvironmentModelImplCopyWithImpl<$Res>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@JsonSerializable()
|
|
||||||
|
@JsonSerializable(explicitToJson: true, anyMap: true)
|
||||||
class _$EnvironmentModelImpl implements _EnvironmentModel {
|
class _$EnvironmentModelImpl implements _EnvironmentModel {
|
||||||
const _$EnvironmentModelImpl(
|
const _$EnvironmentModelImpl(
|
||||||
{required this.id,
|
{required this.id,
|
||||||
this.name = "New Environment",
|
this.name = "",
|
||||||
final List<EnvironmentVariableModel> values = const []})
|
final List<EnvironmentVariableModel> values = const []})
|
||||||
: _values = values;
|
: _values = values;
|
||||||
|
|
||||||
@ -320,7 +321,8 @@ class __$$EnvironmentVariableModelImplCopyWithImpl<$Res>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@JsonSerializable()
|
|
||||||
|
@JsonSerializable(explicitToJson: true, anyMap: true)
|
||||||
class _$EnvironmentVariableModelImpl implements _EnvironmentVariableModel {
|
class _$EnvironmentVariableModelImpl implements _EnvironmentVariableModel {
|
||||||
const _$EnvironmentVariableModelImpl(
|
const _$EnvironmentVariableModelImpl(
|
||||||
{required this.key,
|
{required this.key,
|
||||||
|
@ -6,14 +6,13 @@ part of 'environment_model.dart';
|
|||||||
// JsonSerializableGenerator
|
// JsonSerializableGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
_$EnvironmentModelImpl _$$EnvironmentModelImplFromJson(
|
_$EnvironmentModelImpl _$$EnvironmentModelImplFromJson(Map json) =>
|
||||||
Map<String, dynamic> json) =>
|
|
||||||
_$EnvironmentModelImpl(
|
_$EnvironmentModelImpl(
|
||||||
id: json['id'] as String,
|
id: json['id'] as String,
|
||||||
name: json['name'] as String? ?? "New Environment",
|
name: json['name'] as String? ?? "",
|
||||||
values: (json['values'] as List<dynamic>?)
|
values: (json['values'] as List<dynamic>?)
|
||||||
?.map((e) =>
|
?.map((e) => EnvironmentVariableModel.fromJson(
|
||||||
EnvironmentVariableModel.fromJson(e as Map<String, dynamic>))
|
Map<String, Object?>.from(e as Map)))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
const [],
|
const [],
|
||||||
);
|
);
|
||||||
@ -23,11 +22,11 @@ Map<String, dynamic> _$$EnvironmentModelImplToJson(
|
|||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'id': instance.id,
|
'id': instance.id,
|
||||||
'name': instance.name,
|
'name': instance.name,
|
||||||
'values': instance.values,
|
'values': instance.values.map((e) => e.toJson()).toList(),
|
||||||
};
|
};
|
||||||
|
|
||||||
_$EnvironmentVariableModelImpl _$$EnvironmentVariableModelImplFromJson(
|
_$EnvironmentVariableModelImpl _$$EnvironmentVariableModelImplFromJson(
|
||||||
Map<String, dynamic> json) =>
|
Map json) =>
|
||||||
_$EnvironmentVariableModelImpl(
|
_$EnvironmentVariableModelImpl(
|
||||||
key: json['key'] as String,
|
key: json['key'] as String,
|
||||||
value: json['value'] as String,
|
value: json['value'] as String,
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
import '../consts.dart';
|
import 'providers.dart';
|
||||||
import '../models/models.dart';
|
import '../models/models.dart';
|
||||||
import '../services/services.dart' show hiveHandler, HiveHandler, request;
|
import '../services/services.dart' show hiveHandler, HiveHandler, request;
|
||||||
import '../utils/utils.dart' show getNewUuid, collectionToHAR;
|
import '../utils/utils.dart' show getNewUuid, collectionToHAR;
|
||||||
import 'settings_providers.dart';
|
|
||||||
import 'ui_providers.dart';
|
|
||||||
|
|
||||||
final selectedIdStateProvider = StateProvider<String?>((ref) => null);
|
final selectedIdStateProvider = StateProvider<String?>((ref) => null);
|
||||||
|
|
||||||
|
@ -8,14 +8,13 @@ import '../services/services.dart' show hiveHandler, HiveHandler;
|
|||||||
final selectedEnvironmentIdStateProvider =
|
final selectedEnvironmentIdStateProvider =
|
||||||
StateProvider<String?>((ref) => null);
|
StateProvider<String?>((ref) => null);
|
||||||
|
|
||||||
final environmentsStateNotifierProvider = StateNotifierProvider<
|
final StateNotifierProvider<EnvironmentsStateNotifier,
|
||||||
EnvironmentsStateNotifier, Map<String, EnvironmentModel>?>((ref) {
|
Map<String, EnvironmentModel>?> environmentsStateNotifierProvider =
|
||||||
return EnvironmentsStateNotifier(ref, hiveHandler);
|
StateNotifierProvider((ref) => EnvironmentsStateNotifier(ref, hiveHandler));
|
||||||
});
|
|
||||||
|
|
||||||
final environmentSequenceProvider = StateProvider<List<String>>((ref) {
|
final environmentSequenceProvider = StateProvider<List<String>>((ref) {
|
||||||
var ids = hiveHandler.getEnvironmentIds();
|
var ids = hiveHandler.getEnvironmentIds();
|
||||||
return ids ?? [];
|
return ids ?? [kGlobalEnvironmentId];
|
||||||
});
|
});
|
||||||
|
|
||||||
class EnvironmentsStateNotifier
|
class EnvironmentsStateNotifier
|
||||||
@ -25,7 +24,7 @@ class EnvironmentsStateNotifier
|
|||||||
Future.microtask(() {
|
Future.microtask(() {
|
||||||
if (status) {
|
if (status) {
|
||||||
ref.read(environmentSequenceProvider.notifier).state = [
|
ref.read(environmentSequenceProvider.notifier).state = [
|
||||||
state!.keys.first,
|
...state!.keys,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
ref.read(selectedEnvironmentIdStateProvider.notifier).state =
|
ref.read(selectedEnvironmentIdStateProvider.notifier).state =
|
||||||
@ -52,16 +51,18 @@ class EnvironmentsStateNotifier
|
|||||||
} else {
|
} else {
|
||||||
Map<String, EnvironmentModel> environmentsMap = {};
|
Map<String, EnvironmentModel> environmentsMap = {};
|
||||||
for (var environmentId in environmentIds) {
|
for (var environmentId in environmentIds) {
|
||||||
var environment = hiveHandler.getEnvironment(environmentId);
|
var jsonModel = hiveHandler.getEnvironment(environmentId);
|
||||||
EnvironmentModel environmentModelFromJson =
|
if (jsonModel != null) {
|
||||||
EnvironmentModel.fromJson(environment);
|
var jsonMap = Map<String, Object?>.from(jsonModel);
|
||||||
|
var environmentModelFromJson = EnvironmentModel.fromJson(jsonMap);
|
||||||
|
|
||||||
EnvironmentModel environmentModel = EnvironmentModel(
|
EnvironmentModel environmentModel = EnvironmentModel(
|
||||||
id: environmentModelFromJson.id,
|
id: environmentModelFromJson.id,
|
||||||
name: environmentModelFromJson.name,
|
name: environmentModelFromJson.name,
|
||||||
values: environmentModelFromJson.values,
|
values: environmentModelFromJson.values,
|
||||||
);
|
);
|
||||||
environmentsMap[environmentId] = environmentModel;
|
environmentsMap[environmentId] = environmentModel;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
state = environmentsMap;
|
state = environmentsMap;
|
||||||
return true;
|
return true;
|
||||||
@ -160,10 +161,10 @@ class EnvironmentsStateNotifier
|
|||||||
ref.read(hasUnsavedChangesProvider.notifier).state = true;
|
ref.read(hasUnsavedChangesProvider.notifier).state = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveEnvironments() async {
|
Future<void> saveEnvironments() async {
|
||||||
ref.read(saveDataStateProvider.notifier).state = true;
|
ref.read(saveDataStateProvider.notifier).state = true;
|
||||||
final environmentIds = ref.read(environmentSequenceProvider);
|
final environmentIds = ref.read(environmentSequenceProvider);
|
||||||
hiveHandler.setEnvironmentIds(environmentIds);
|
await hiveHandler.setEnvironmentIds(environmentIds);
|
||||||
for (var environmentId in environmentIds) {
|
for (var environmentId in environmentIds) {
|
||||||
var environment = state![environmentId]!;
|
var environment = state![environmentId]!;
|
||||||
await hiveHandler.setEnvironment(environmentId, environment.toJson());
|
await hiveHandler.setEnvironment(environmentId, environment.toJson());
|
||||||
|
112
lib/screens/common/sidebar_widgets.dart
Normal file
112
lib/screens/common/sidebar_widgets.dart
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
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/consts.dart';
|
||||||
|
|
||||||
|
class SidebarHeader extends StatelessWidget {
|
||||||
|
const SidebarHeader({super.key, this.onAddNew});
|
||||||
|
final Function()? onAddNew;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: kPe8,
|
||||||
|
child: Wrap(
|
||||||
|
alignment: WrapAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
const SaveButton(),
|
||||||
|
//const Spacer(),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: onAddNew,
|
||||||
|
child: const Text(
|
||||||
|
kLabelPlusNew,
|
||||||
|
style: kTextStyleButton,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SidebarFilter extends StatelessWidget {
|
||||||
|
const SidebarFilter({
|
||||||
|
super.key,
|
||||||
|
this.onFilterFieldChanged,
|
||||||
|
this.filterHintText,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Function(String)? onFilterFieldChanged;
|
||||||
|
final String? filterHintText;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.only(right: 8),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: kBorderRadius8,
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
kHSpacer5,
|
||||||
|
Icon(
|
||||||
|
Icons.filter_alt,
|
||||||
|
size: 18,
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
kHSpacer5,
|
||||||
|
Expanded(
|
||||||
|
child: RawTextField(
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium,
|
||||||
|
hintText: filterHintText ?? "Filter by name",
|
||||||
|
onChanged: onFilterFieldChanged,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SaveButton extends ConsumerWidget {
|
||||||
|
const SaveButton({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final overlayWidget = OverlayWidgetTemplate(context: context);
|
||||||
|
final savingData = ref.watch(saveDataStateProvider);
|
||||||
|
final hasUnsavedChanges = ref.watch(hasUnsavedChangesProvider);
|
||||||
|
return TextButton.icon(
|
||||||
|
onPressed: (savingData || !hasUnsavedChanges)
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
overlayWidget.show(
|
||||||
|
widget: const SavingOverlay(saveCompleted: false));
|
||||||
|
|
||||||
|
await ref
|
||||||
|
.read(collectionStateNotifierProvider.notifier)
|
||||||
|
.saveData();
|
||||||
|
await ref
|
||||||
|
.read(environmentsStateNotifierProvider.notifier)
|
||||||
|
.saveEnvironments();
|
||||||
|
overlayWidget.hide();
|
||||||
|
overlayWidget.show(
|
||||||
|
widget: const SavingOverlay(saveCompleted: true));
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
overlayWidget.hide();
|
||||||
|
},
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.save,
|
||||||
|
size: 20,
|
||||||
|
),
|
||||||
|
label: const Text(
|
||||||
|
kLabelSave,
|
||||||
|
style: kTextStyleButton,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ class Dashboard extends ConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final railIdx = ref.watch(navRailIndexStateProvider);
|
final railIdx = ref.watch(navRailIndexStateProvider);
|
||||||
|
final mobileScaffoldKey = ref.watch(mobileScaffoldKeyStateProvider);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -93,11 +94,13 @@ class Dashboard extends ConsumerWidget {
|
|||||||
child: IndexedStack(
|
child: IndexedStack(
|
||||||
alignment: AlignmentDirectional.topCenter,
|
alignment: AlignmentDirectional.topCenter,
|
||||||
index: railIdx,
|
index: railIdx,
|
||||||
children: const [
|
children: [
|
||||||
HomePage(),
|
const HomePage(),
|
||||||
EnvironmentPage(),
|
EnvironmentPage(
|
||||||
IntroPage(),
|
scaffoldKey: mobileScaffoldKey,
|
||||||
SettingsPage(),
|
),
|
||||||
|
const IntroPage(),
|
||||||
|
const SettingsPage(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -1,12 +1,29 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:apidash/widgets/widgets.dart';
|
import 'package:apidash/widgets/widgets.dart';
|
||||||
|
import 'package:apidash/extensions/extensions.dart';
|
||||||
|
import 'package:apidash/providers/providers.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'environments_pane.dart';
|
import 'environments_pane.dart';
|
||||||
|
|
||||||
class EnvironmentPage extends StatelessWidget {
|
class EnvironmentPage extends ConsumerWidget {
|
||||||
const EnvironmentPage({super.key});
|
const EnvironmentPage({
|
||||||
|
super.key,
|
||||||
|
required this.scaffoldKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
final GlobalKey<ScaffoldState> scaffoldKey;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
if (context.isMediumWindow) {
|
||||||
|
return TwoDrawerScaffold(
|
||||||
|
scaffoldKey: scaffoldKey,
|
||||||
|
mainContent: const SizedBox(), // TODO: replace placeholder
|
||||||
|
title: const Text("Environments"), // TODO: replace placeholder
|
||||||
|
leftDrawerContent: const EnvironmentsPane(),
|
||||||
|
onDrawerChanged: (value) =>
|
||||||
|
ref.read(leftDrawerStateProvider.notifier).state = value,
|
||||||
|
);
|
||||||
|
}
|
||||||
return const Column(
|
return const Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import 'package:apidash/consts.dart';
|
|
||||||
import 'package:apidash/extensions/extensions.dart';
|
|
||||||
import 'package:apidash/models/environment_model.dart';
|
|
||||||
import 'package:apidash/providers/providers.dart';
|
|
||||||
import 'package:apidash/widgets/cards.dart';
|
|
||||||
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:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:apidash/extensions/extensions.dart';
|
||||||
|
import 'package:apidash/models/environment_model.dart';
|
||||||
|
import 'package:apidash/providers/providers.dart';
|
||||||
|
import 'package:apidash/widgets/widgets.dart';
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
|
import 'package:apidash/screens/common/sidebar_widgets.dart';
|
||||||
|
|
||||||
class EnvironmentsPane extends ConsumerWidget {
|
class EnvironmentsPane extends ConsumerWidget {
|
||||||
const EnvironmentsPane({
|
const EnvironmentsPane({
|
||||||
@ -15,30 +16,31 @@ class EnvironmentsPane extends ConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: kIsMacOS ? kP24CollectionPane : kP8CollectionPane,
|
padding: (!context.isMediumWindow && kIsMacOS
|
||||||
|
? kP24CollectionPane
|
||||||
|
: kP8CollectionPane) +
|
||||||
|
(context.isMediumWindow ? kPb70 : EdgeInsets.zero),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
SidebarHeader(
|
||||||
padding: kPe8,
|
onAddNew: () {
|
||||||
child: Wrap(
|
ref
|
||||||
alignment: WrapAlignment.spaceBetween,
|
.read(environmentsStateNotifierProvider.notifier)
|
||||||
children: [
|
.addEnvironment();
|
||||||
ElevatedButton(
|
},
|
||||||
onPressed: () {
|
|
||||||
ref
|
|
||||||
.read(environmentsStateNotifierProvider.notifier)
|
|
||||||
.addEnvironment();
|
|
||||||
},
|
|
||||||
child: const Text(
|
|
||||||
kLabelPlusNew,
|
|
||||||
style: kTextStyleButton,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
kVSpacer10,
|
||||||
|
SidebarFilter(
|
||||||
|
filterHintText: "Filter by name",
|
||||||
|
onFilterFieldChanged: (value) {
|
||||||
|
ref.read(environmentSearchQueryProvider.notifier).state =
|
||||||
|
value.toLowerCase();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
kVSpacer10,
|
||||||
const Expanded(child: EnvironmentsList()),
|
const Expanded(child: EnvironmentsList()),
|
||||||
|
kVSpacer5
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -59,81 +61,105 @@ class EnvironmentsList extends HookConsumerWidget {
|
|||||||
final filterQuery = ref.watch(environmentSearchQueryProvider).trim();
|
final filterQuery = ref.watch(environmentSearchQueryProvider).trim();
|
||||||
|
|
||||||
ScrollController scrollController = useScrollController();
|
ScrollController scrollController = useScrollController();
|
||||||
return Scrollbar(
|
return Column(
|
||||||
controller: scrollController,
|
children: [
|
||||||
thumbVisibility: alwaysShowEnvironmentsPaneScrollbar,
|
Padding(
|
||||||
radius: const Radius.circular(12),
|
padding: kP1 + kPe8,
|
||||||
child: filterQuery.isEmpty
|
child: EnvironmentItem(
|
||||||
? ReorderableListView.builder(
|
id: kGlobalEnvironmentId,
|
||||||
padding: context.isMediumWindow
|
environmentModel: environmentItems[kGlobalEnvironmentId]!,
|
||||||
? EdgeInsets.only(
|
),
|
||||||
bottom: MediaQuery.paddingOf(context).bottom,
|
),
|
||||||
right: 8,
|
const Divider(endIndent: 4),
|
||||||
)
|
Expanded(
|
||||||
: kPe8,
|
child: Scrollbar(
|
||||||
scrollController: scrollController,
|
controller: scrollController,
|
||||||
buildDefaultDragHandles: false,
|
thumbVisibility: alwaysShowEnvironmentsPaneScrollbar,
|
||||||
itemCount: environmentSequence.length,
|
radius: const Radius.circular(12),
|
||||||
onReorder: (int oldIndex, int newIndex) {
|
child: filterQuery.isEmpty
|
||||||
if (oldIndex < newIndex) {
|
? ReorderableListView.builder(
|
||||||
newIndex -= 1;
|
padding: context.isMediumWindow
|
||||||
}
|
? EdgeInsets.only(
|
||||||
if (oldIndex != newIndex) {
|
bottom: MediaQuery.paddingOf(context).bottom,
|
||||||
ref
|
right: 8,
|
||||||
.read(collectionStateNotifierProvider.notifier)
|
)
|
||||||
.reorder(oldIndex, newIndex);
|
: kPe8,
|
||||||
}
|
scrollController: scrollController,
|
||||||
},
|
buildDefaultDragHandles: false,
|
||||||
itemBuilder: (context, index) {
|
itemCount: environmentSequence.length,
|
||||||
var id = environmentSequence[index];
|
onReorder: (int oldIndex, int newIndex) {
|
||||||
if (kIsMobile) {
|
if (oldIndex < newIndex) {
|
||||||
return ReorderableDelayedDragStartListener(
|
newIndex -= 1;
|
||||||
key: ValueKey(id),
|
}
|
||||||
index: index,
|
if (oldIndex != newIndex) {
|
||||||
child: Padding(
|
ref
|
||||||
padding: kP1,
|
.read(environmentsStateNotifierProvider.notifier)
|
||||||
child: EnvironmentItem(
|
.reorder(oldIndex, newIndex);
|
||||||
id: id,
|
}
|
||||||
environmentModel: environmentItems[id]!,
|
},
|
||||||
),
|
itemBuilder: (context, index) {
|
||||||
),
|
var id = environmentSequence[index];
|
||||||
);
|
if (id == kGlobalEnvironmentId) {
|
||||||
}
|
return SizedBox.shrink(
|
||||||
return ReorderableDragStartListener(
|
key: ValueKey(id),
|
||||||
key: ValueKey(id),
|
);
|
||||||
index: index,
|
}
|
||||||
child: Padding(
|
if (kIsMobile) {
|
||||||
padding: kP1,
|
return ReorderableDelayedDragStartListener(
|
||||||
child: EnvironmentItem(
|
key: ValueKey(id),
|
||||||
id: id,
|
index: index,
|
||||||
environmentModel: environmentItems[id]!,
|
child: Padding(
|
||||||
),
|
padding: kP1,
|
||||||
|
child: EnvironmentItem(
|
||||||
|
id: id,
|
||||||
|
environmentModel: environmentItems[id]!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ReorderableDragStartListener(
|
||||||
|
key: ValueKey(id),
|
||||||
|
index: index,
|
||||||
|
child: Padding(
|
||||||
|
padding: kP1,
|
||||||
|
child: EnvironmentItem(
|
||||||
|
id: id,
|
||||||
|
environmentModel: environmentItems[id]!,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: ListView(
|
||||||
|
padding: context.isMediumWindow
|
||||||
|
? EdgeInsets.only(
|
||||||
|
bottom: MediaQuery.paddingOf(context).bottom,
|
||||||
|
right: 8,
|
||||||
|
)
|
||||||
|
: kPe8,
|
||||||
|
controller: scrollController,
|
||||||
|
children: environmentSequence.map((id) {
|
||||||
|
var item = environmentItems[id]!;
|
||||||
|
if (id == kGlobalEnvironmentId) {
|
||||||
|
return SizedBox.shrink(
|
||||||
|
key: ValueKey(id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (item.name.toLowerCase().contains(filterQuery)) {
|
||||||
|
return Padding(
|
||||||
|
padding: kP1,
|
||||||
|
child: EnvironmentItem(
|
||||||
|
id: id,
|
||||||
|
environmentModel: item,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return const SizedBox();
|
||||||
|
}).toList(),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
),
|
||||||
)
|
],
|
||||||
: ListView(
|
|
||||||
padding: context.isMediumWindow
|
|
||||||
? EdgeInsets.only(
|
|
||||||
bottom: MediaQuery.paddingOf(context).bottom,
|
|
||||||
right: 8,
|
|
||||||
)
|
|
||||||
: kPe8,
|
|
||||||
controller: scrollController,
|
|
||||||
children: environmentSequence.map((id) {
|
|
||||||
var item = environmentItems[id]!;
|
|
||||||
if (item.name.toLowerCase().contains(filterQuery)) {
|
|
||||||
return Padding(
|
|
||||||
padding: kP1,
|
|
||||||
child: EnvironmentItem(
|
|
||||||
id: id,
|
|
||||||
environmentModel: item,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return const SizedBox();
|
|
||||||
}).toList(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,7 +202,29 @@ class EnvironmentItem extends ConsumerWidget {
|
|||||||
.updateEnvironment(editRequestId!, name: value);
|
.updateEnvironment(editRequestId!, name: value);
|
||||||
},
|
},
|
||||||
onTapOutsideNameEditor: () {
|
onTapOutsideNameEditor: () {
|
||||||
ref.read(selectedEnvironmentIdStateProvider.notifier).state = null;
|
ref.read(selectedIdEditStateProvider.notifier).state = null;
|
||||||
|
},
|
||||||
|
onMenuSelected: (ItemMenuOption item) {
|
||||||
|
if (item == ItemMenuOption.edit) {
|
||||||
|
ref.read(selectedIdEditStateProvider.notifier).state = id;
|
||||||
|
Future.delayed(
|
||||||
|
const Duration(milliseconds: 150),
|
||||||
|
() => ref
|
||||||
|
.read(nameTextFieldFocusNodeProvider.notifier)
|
||||||
|
.state
|
||||||
|
.requestFocus(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (item == ItemMenuOption.delete) {
|
||||||
|
ref
|
||||||
|
.read(environmentsStateNotifierProvider.notifier)
|
||||||
|
.removeEnvironment(id);
|
||||||
|
}
|
||||||
|
if (item == ItemMenuOption.duplicate) {
|
||||||
|
ref
|
||||||
|
.read(environmentsStateNotifierProvider.notifier)
|
||||||
|
.duplicateEnvironment(id);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
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: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/models/models.dart';
|
import 'package:apidash/models/models.dart';
|
||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
import 'package:apidash/extensions/extensions.dart';
|
import 'package:apidash/screens/common/sidebar_widgets.dart';
|
||||||
|
|
||||||
class CollectionPane extends ConsumerWidget {
|
class CollectionPane extends ConsumerWidget {
|
||||||
const CollectionPane({
|
const CollectionPane({
|
||||||
@ -13,110 +14,39 @@ class CollectionPane extends ConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final overlayWidget = OverlayWidgetTemplate(context: context);
|
|
||||||
final collection = ref.watch(collectionStateNotifierProvider);
|
final collection = ref.watch(collectionStateNotifierProvider);
|
||||||
final savingData = ref.watch(saveDataStateProvider);
|
|
||||||
final hasUnsavedChanges = ref.watch(hasUnsavedChangesProvider);
|
|
||||||
if (collection == null) {
|
if (collection == null) {
|
||||||
return const Center(
|
return const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Drawer(
|
return Padding(
|
||||||
shape: const ContinuousRectangleBorder(),
|
padding: (!context.isMediumWindow && kIsMacOS
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
? kP24CollectionPane
|
||||||
surfaceTintColor: kColorTransparent,
|
: kP8CollectionPane) +
|
||||||
child: Padding(
|
(context.isMediumWindow ? kPb70 : EdgeInsets.zero),
|
||||||
padding: (!context.isMediumWindow && kIsMacOS
|
child: Column(
|
||||||
? kP24CollectionPane
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
: kP8CollectionPane) +
|
children: [
|
||||||
(context.isMediumWindow ? kPb70 : EdgeInsets.zero),
|
SidebarHeader(
|
||||||
child: Column(
|
onAddNew: () {
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
ref.read(collectionStateNotifierProvider.notifier).add();
|
||||||
children: [
|
},
|
||||||
Padding(
|
),
|
||||||
padding: kPe8,
|
kVSpacer10,
|
||||||
child: Wrap(
|
SidebarFilter(
|
||||||
alignment: WrapAlignment.spaceBetween,
|
filterHintText: "Filter by name or url",
|
||||||
children: [
|
onFilterFieldChanged: (value) {
|
||||||
TextButton.icon(
|
ref.read(collectionSearchQueryProvider.notifier).state =
|
||||||
onPressed: (savingData || !hasUnsavedChanges)
|
value.toLowerCase();
|
||||||
? null
|
},
|
||||||
: () async {
|
),
|
||||||
overlayWidget.show(
|
kVSpacer10,
|
||||||
widget:
|
const Expanded(
|
||||||
const SavingOverlay(saveCompleted: false));
|
child: RequestList(),
|
||||||
|
),
|
||||||
await ref
|
kVSpacer5
|
||||||
.read(collectionStateNotifierProvider.notifier)
|
],
|
||||||
.saveData();
|
|
||||||
overlayWidget.hide();
|
|
||||||
overlayWidget.show(
|
|
||||||
widget:
|
|
||||||
const SavingOverlay(saveCompleted: true));
|
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
|
||||||
overlayWidget.hide();
|
|
||||||
},
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.save,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
label: const Text(
|
|
||||||
kLabelSave,
|
|
||||||
style: kTextStyleButton,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
//const Spacer(),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
ref.read(collectionStateNotifierProvider.notifier).add();
|
|
||||||
},
|
|
||||||
child: const Text(
|
|
||||||
kLabelPlusNew,
|
|
||||||
style: kTextStyleButton,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
kVSpacer10,
|
|
||||||
Container(
|
|
||||||
margin: const EdgeInsets.only(right: 8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: kBorderRadius8,
|
|
||||||
border: Border.all(
|
|
||||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
kHSpacer5,
|
|
||||||
Icon(
|
|
||||||
Icons.filter_alt,
|
|
||||||
size: 18,
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
kHSpacer5,
|
|
||||||
Expanded(
|
|
||||||
child: RawTextField(
|
|
||||||
style: Theme.of(context).textTheme.bodyMedium,
|
|
||||||
hintText: "Filter by name or URL",
|
|
||||||
onChanged: (value) {
|
|
||||||
ref.read(collectionSearchQueryProvider.notifier).state =
|
|
||||||
value.toLowerCase();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
kVSpacer10,
|
|
||||||
const Expanded(
|
|
||||||
child: RequestList(),
|
|
||||||
),
|
|
||||||
kVSpacer5
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -277,8 +207,8 @@ class RequestItem extends ConsumerWidget {
|
|||||||
onTapOutsideNameEditor: () {
|
onTapOutsideNameEditor: () {
|
||||||
ref.read(selectedIdEditStateProvider.notifier).state = null;
|
ref.read(selectedIdEditStateProvider.notifier).state = null;
|
||||||
},
|
},
|
||||||
onMenuSelected: (RequestItemMenuOption item) {
|
onMenuSelected: (ItemMenuOption item) {
|
||||||
if (item == RequestItemMenuOption.edit) {
|
if (item == ItemMenuOption.edit) {
|
||||||
// var controller =
|
// var controller =
|
||||||
// ref.read(nameTextFieldControllerProvider.notifier).state;
|
// ref.read(nameTextFieldControllerProvider.notifier).state;
|
||||||
// controller.text = requestModel.name;
|
// controller.text = requestModel.name;
|
||||||
@ -294,10 +224,10 @@ class RequestItem extends ConsumerWidget {
|
|||||||
.requestFocus(),
|
.requestFocus(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (item == RequestItemMenuOption.delete) {
|
if (item == ItemMenuOption.delete) {
|
||||||
ref.read(collectionStateNotifierProvider.notifier).remove(id);
|
ref.read(collectionStateNotifierProvider.notifier).remove(id);
|
||||||
}
|
}
|
||||||
if (item == RequestItemMenuOption.duplicate) {
|
if (item == ItemMenuOption.duplicate) {
|
||||||
ref.read(collectionStateNotifierProvider.notifier).duplicate(id);
|
ref.read(collectionStateNotifierProvider.notifier).duplicate(id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:apidash/consts.dart';
|
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';
|
||||||
@ -69,19 +69,8 @@ class PageBranch extends ConsumerWidget {
|
|||||||
switch (pageIndex) {
|
switch (pageIndex) {
|
||||||
case 1:
|
case 1:
|
||||||
// Temporary Environment Page
|
// Temporary Environment Page
|
||||||
return Scaffold(
|
return EnvironmentPage(
|
||||||
key: scaffoldKey,
|
scaffoldKey: scaffoldKey,
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text('Environments'),
|
|
||||||
),
|
|
||||||
onDrawerChanged: (isOpened) {
|
|
||||||
ref.read(leftDrawerStateProvider.notifier).state = isOpened;
|
|
||||||
},
|
|
||||||
drawer: const Drawer(
|
|
||||||
surfaceTintColor: kColorTransparent,
|
|
||||||
shape: ContinuousRectangleBorder(),
|
|
||||||
),
|
|
||||||
body: const SizedBox(),
|
|
||||||
);
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return const PageBase(
|
return const PageBase(
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
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:apidash/extensions/extensions.dart';
|
|
||||||
import 'package:apidash/providers/providers.dart';
|
import 'package:apidash/providers/providers.dart';
|
||||||
|
|
||||||
class BottomNavBar extends ConsumerWidget {
|
class BottomNavBar extends ConsumerWidget {
|
||||||
|
@ -47,7 +47,7 @@ class RequestTitle extends ConsumerWidget {
|
|||||||
borderRadius: BorderRadius.circular(8),
|
borderRadius: BorderRadius.circular(8),
|
||||||
child: Material(
|
child: Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
child: RequestCardMenu(
|
child: ItemCardMenu(
|
||||||
offset: const Offset(0, 40),
|
offset: const Offset(0, 40),
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||||
splashRadius: 0,
|
splashRadius: 0,
|
||||||
@ -75,18 +75,18 @@ class RequestTitle extends ConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onSelected: (RequestItemMenuOption item) {
|
onSelected: (ItemMenuOption item) {
|
||||||
if (item == RequestItemMenuOption.edit) {
|
if (item == ItemMenuOption.edit) {
|
||||||
showRenameDialog(context, name, (val) {
|
showRenameDialog(context, name, (val) {
|
||||||
ref
|
ref
|
||||||
.read(collectionStateNotifierProvider.notifier)
|
.read(collectionStateNotifierProvider.notifier)
|
||||||
.update(id!, name: val);
|
.update(id!, name: val);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (item == RequestItemMenuOption.delete) {
|
if (item == ItemMenuOption.delete) {
|
||||||
ref.read(collectionStateNotifierProvider.notifier).remove(id!);
|
ref.read(collectionStateNotifierProvider.notifier).remove(id!);
|
||||||
}
|
}
|
||||||
if (item == RequestItemMenuOption.duplicate) {
|
if (item == ItemMenuOption.duplicate) {
|
||||||
ref.read(collectionStateNotifierProvider.notifier).duplicate(id!);
|
ref.read(collectionStateNotifierProvider.notifier).duplicate(id!);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
6
lib/utils/envvar_utils.dart
Normal file
6
lib/utils/envvar_utils.dart
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
String getEnvironmentTitle(String? name) {
|
||||||
|
if (name == null || name.trim() == "") {
|
||||||
|
return "untitled";
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
@ -4,3 +4,4 @@ export 'http_utils.dart';
|
|||||||
export 'file_utils.dart';
|
export 'file_utils.dart';
|
||||||
export 'window_utils.dart';
|
export 'window_utils.dart';
|
||||||
export 'har_utils.dart';
|
export 'har_utils.dart';
|
||||||
|
export 'envvar_utils.dart';
|
||||||
|
@ -212,30 +212,6 @@ class DiscordButton extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SaveButton extends StatelessWidget {
|
|
||||||
const SaveButton({
|
|
||||||
super.key,
|
|
||||||
this.onPressed,
|
|
||||||
});
|
|
||||||
|
|
||||||
final VoidCallback? onPressed;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return TextButton.icon(
|
|
||||||
onPressed: onPressed,
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.save,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
label: const Text(
|
|
||||||
kLabelSave,
|
|
||||||
style: kTextStyleButton,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ClearResponseButton extends StatelessWidget {
|
class ClearResponseButton extends StatelessWidget {
|
||||||
const ClearResponseButton({
|
const ClearResponseButton({
|
||||||
super.key,
|
super.key,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
import 'package:apidash/utils/utils.dart';
|
import 'package:apidash/utils/utils.dart';
|
||||||
import 'menus.dart' show RequestCardMenu;
|
import 'menus.dart' show ItemCardMenu;
|
||||||
import 'texts.dart' show MethodBox;
|
import 'texts.dart' show MethodBox;
|
||||||
|
|
||||||
class SidebarRequestCard extends StatelessWidget {
|
class SidebarRequestCard extends StatelessWidget {
|
||||||
@ -36,7 +36,7 @@ class SidebarRequestCard extends StatelessWidget {
|
|||||||
// final TextEditingController? controller;
|
// final TextEditingController? controller;
|
||||||
final FocusNode? focusNode;
|
final FocusNode? focusNode;
|
||||||
final Function()? onTapOutsideNameEditor;
|
final Function()? onTapOutsideNameEditor;
|
||||||
final Function(RequestItemMenuOption)? onMenuSelected;
|
final Function(ItemMenuOption)? onMenuSelected;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -118,7 +118,7 @@ class SidebarRequestCard extends StatelessWidget {
|
|||||||
visible: isSelected && !inEditMode,
|
visible: isSelected && !inEditMode,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 28,
|
width: 28,
|
||||||
child: RequestCardMenu(
|
child: ItemCardMenu(
|
||||||
onSelected: onMenuSelected,
|
onSelected: onMenuSelected,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -185,19 +185,19 @@ class SidebarEnvironmentCard extends StatelessWidget {
|
|||||||
final Function(String)? onChangedNameEditor;
|
final Function(String)? onChangedNameEditor;
|
||||||
final FocusNode? focusNode;
|
final FocusNode? focusNode;
|
||||||
final Function()? onTapOutsideNameEditor;
|
final Function()? onTapOutsideNameEditor;
|
||||||
final Function(RequestItemMenuOption)? onMenuSelected;
|
final Function(ItemMenuOption)? onMenuSelected;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final Color color = Theme.of(context).colorScheme.surface;
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
final Color colorVariant =
|
final Color color = colorScheme.surface;
|
||||||
Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5);
|
final Color colorVariant = colorScheme.surfaceVariant.withOpacity(0.5);
|
||||||
final Color surfaceTint = Theme.of(context).colorScheme.primary;
|
final Color surfaceTint = colorScheme.primary;
|
||||||
bool isSelected = selectedId == id;
|
bool isSelected = selectedId == id;
|
||||||
bool inEditMode = editRequestId == id;
|
bool inEditMode = editRequestId == id;
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
String nm = getEnvironmentTitle(name);
|
||||||
return Tooltip(
|
return Tooltip(
|
||||||
message: name,
|
message: nm,
|
||||||
triggerMode: TooltipTriggerMode.manual,
|
triggerMode: TooltipTriggerMode.manual,
|
||||||
waitDuration: const Duration(seconds: 1),
|
waitDuration: const Duration(seconds: 1),
|
||||||
child: Card(
|
child: Card(
|
||||||
@ -230,22 +230,6 @@ class SidebarEnvironmentCard extends StatelessWidget {
|
|||||||
height: 20,
|
height: 20,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
isGlobal
|
|
||||||
? const SizedBox.shrink()
|
|
||||||
: Checkbox(
|
|
||||||
value: isActive,
|
|
||||||
onChanged: isActive ? null : setActive,
|
|
||||||
shape: const CircleBorder(),
|
|
||||||
checkColor: colorScheme.onPrimary,
|
|
||||||
fillColor: MaterialStateProperty.resolveWith<Color?>(
|
|
||||||
(Set<MaterialState> states) {
|
|
||||||
if (states.contains(MaterialState.selected)) {
|
|
||||||
return colorScheme.primary;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
kHSpacer4,
|
kHSpacer4,
|
||||||
Expanded(
|
Expanded(
|
||||||
child: inEditMode
|
child: inEditMode
|
||||||
@ -271,16 +255,16 @@ class SidebarEnvironmentCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Text(
|
: Text(
|
||||||
name ?? "h",
|
nm,
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: isSelected && !inEditMode,
|
visible: isSelected && !inEditMode && !isGlobal,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 28,
|
width: 28,
|
||||||
child: RequestCardMenu(
|
child: ItemCardMenu(
|
||||||
onSelected: onMenuSelected,
|
onSelected: onMenuSelected,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
|
|
||||||
class RequestCardMenu extends StatelessWidget {
|
class ItemCardMenu extends StatelessWidget {
|
||||||
const RequestCardMenu({
|
const ItemCardMenu({
|
||||||
super.key,
|
super.key,
|
||||||
this.onSelected,
|
this.onSelected,
|
||||||
this.child,
|
this.child,
|
||||||
@ -17,11 +17,11 @@ class RequestCardMenu extends StatelessWidget {
|
|||||||
final String? tooltip;
|
final String? tooltip;
|
||||||
final ShapeBorder? shape;
|
final ShapeBorder? shape;
|
||||||
|
|
||||||
final Function(RequestItemMenuOption)? onSelected;
|
final Function(ItemMenuOption)? onSelected;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PopupMenuButton<RequestItemMenuOption>(
|
return PopupMenuButton<ItemMenuOption>(
|
||||||
tooltip: tooltip,
|
tooltip: tooltip,
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
splashRadius: splashRadius,
|
splashRadius: splashRadius,
|
||||||
@ -29,18 +29,17 @@ class RequestCardMenu extends StatelessWidget {
|
|||||||
offset: offset,
|
offset: offset,
|
||||||
onSelected: onSelected,
|
onSelected: onSelected,
|
||||||
shape: shape,
|
shape: shape,
|
||||||
itemBuilder: (BuildContext context) =>
|
itemBuilder: (BuildContext context) => <PopupMenuEntry<ItemMenuOption>>[
|
||||||
<PopupMenuEntry<RequestItemMenuOption>>[
|
const PopupMenuItem<ItemMenuOption>(
|
||||||
const PopupMenuItem<RequestItemMenuOption>(
|
value: ItemMenuOption.edit,
|
||||||
value: RequestItemMenuOption.edit,
|
|
||||||
child: Text('Rename'),
|
child: Text('Rename'),
|
||||||
),
|
),
|
||||||
const PopupMenuItem<RequestItemMenuOption>(
|
const PopupMenuItem<ItemMenuOption>(
|
||||||
value: RequestItemMenuOption.delete,
|
value: ItemMenuOption.delete,
|
||||||
child: Text('Delete'),
|
child: Text('Delete'),
|
||||||
),
|
),
|
||||||
const PopupMenuItem<RequestItemMenuOption>(
|
const PopupMenuItem<ItemMenuOption>(
|
||||||
value: RequestItemMenuOption.duplicate,
|
value: ItemMenuOption.duplicate,
|
||||||
child: Text('Duplicate'),
|
child: Text('Duplicate'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -179,7 +179,12 @@ class TwoDrawerScaffold extends StatelessWidget {
|
|||||||
: const SizedBox.shrink()),
|
: const SizedBox.shrink()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
drawer: leftDrawerContent,
|
drawer: Drawer(
|
||||||
|
shape: const ContinuousRectangleBorder(),
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
|
surfaceTintColor: kColorTransparent,
|
||||||
|
child: leftDrawerContent,
|
||||||
|
),
|
||||||
endDrawer: rightDrawerContent,
|
endDrawer: rightDrawerContent,
|
||||||
body: mainContent,
|
body: mainContent,
|
||||||
bottomNavigationBar: bottomNavigationBar,
|
bottomNavigationBar: bottomNavigationBar,
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:typed_data';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:apidash/widgets/buttons.dart';
|
import 'package:apidash/widgets/buttons.dart';
|
||||||
|
import 'package:apidash/screens/common/sidebar_widgets.dart';
|
||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
import '../test_consts.dart';
|
import '../test_consts.dart';
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import 'package:apidash/consts.dart';
|
|||||||
import '../test_consts.dart';
|
import '../test_consts.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWidgets('Testing RequestCardMenu', (tester) async {
|
testWidgets('Testing ItemCardMenu', (tester) async {
|
||||||
dynamic changedValue;
|
dynamic changedValue;
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
MaterialApp(
|
MaterialApp(
|
||||||
@ -15,7 +15,7 @@ void main() {
|
|||||||
body: Center(
|
body: Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
RequestCardMenu(
|
ItemCardMenu(
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
changedValue = value;
|
changedValue = value;
|
||||||
},
|
},
|
||||||
@ -27,9 +27,9 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(find.byType(PopupMenuButton<RequestItemMenuOption>), findsOneWidget);
|
expect(find.byType(PopupMenuButton<ItemMenuOption>), findsOneWidget);
|
||||||
|
|
||||||
await tester.tap(find.byType(PopupMenuButton<RequestItemMenuOption>));
|
await tester.tap(find.byType(PopupMenuButton<ItemMenuOption>));
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pump(const Duration(seconds: 1));
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
|
||||||
@ -37,9 +37,9 @@ void main() {
|
|||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pump(const Duration(seconds: 1));
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
|
||||||
expect(changedValue, RequestItemMenuOption.delete);
|
expect(changedValue, ItemMenuOption.delete);
|
||||||
|
|
||||||
await tester.tap(find.byType(PopupMenuButton<RequestItemMenuOption>));
|
await tester.tap(find.byType(PopupMenuButton<ItemMenuOption>));
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pump(const Duration(seconds: 1));
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
|
||||||
@ -47,6 +47,6 @@ void main() {
|
|||||||
await tester.pump();
|
await tester.pump();
|
||||||
await tester.pump(const Duration(seconds: 1));
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
|
||||||
expect(changedValue, RequestItemMenuOption.duplicate);
|
expect(changedValue, ItemMenuOption.duplicate);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user