mirror of
https://github.com/foss42/apidash.git
synced 2025-09-28 01:23:47 +08:00
Introducing the new text editor 🎉
This commit is contained in:
@ -1,20 +1,28 @@
|
|||||||
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/widgets/editor.dart';
|
||||||
import 'package:apidash/consts.dart';
|
import 'package:apidash/consts.dart';
|
||||||
|
|
||||||
class EditRequestBody extends StatefulWidget {
|
class EditRequestBody extends ConsumerStatefulWidget {
|
||||||
const EditRequestBody({super.key});
|
const EditRequestBody({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<EditRequestBody> createState() => _EditRequestBodyState();
|
ConsumerState<EditRequestBody> createState() => _EditRequestBodyState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EditRequestBodyState extends State<EditRequestBody> {
|
class _EditRequestBodyState extends ConsumerState<EditRequestBody> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final activeId = ref.watch(activeIdStateProvider);
|
||||||
|
final reqestModel = ref
|
||||||
|
.read(collectionStateNotifierProvider.notifier)
|
||||||
|
.getRequestModel(activeId!);
|
||||||
return Container(
|
return Container(
|
||||||
decoration: kTableContainerDecoration,
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).colorScheme.background,
|
||||||
|
borderRadius: kBorder12,
|
||||||
|
),
|
||||||
margin: kP5,
|
margin: kP5,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
@ -31,8 +39,16 @@ class _EditRequestBodyState extends State<EditRequestBody> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Expanded(
|
Expanded(
|
||||||
child: TextFieldEditor(),
|
child: TextFieldEditor(
|
||||||
|
fieldKey: "$activeId-body",
|
||||||
|
initialValue: reqestModel.requestBody,
|
||||||
|
onChanged: (String value) {
|
||||||
|
ref
|
||||||
|
.read(collectionStateNotifierProvider.notifier)
|
||||||
|
.update(activeId, requestBody: value);
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -54,13 +70,14 @@ class _DropdownButtonBodyContentTypeState
|
|||||||
extends ConsumerState<DropdownButtonBodyContentType> {
|
extends ConsumerState<DropdownButtonBodyContentType> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final surfaceColor = Theme.of(context).colorScheme.surface;
|
||||||
final activeId = ref.watch(activeIdStateProvider);
|
final activeId = ref.watch(activeIdStateProvider);
|
||||||
final collection = ref.read(collectionStateNotifierProvider);
|
final collection = ref.read(collectionStateNotifierProvider);
|
||||||
final idIdx = collection.indexWhere((m) => m.id == activeId);
|
final idIdx = collection.indexWhere((m) => m.id == activeId);
|
||||||
final requestBodyContentType = ref.watch(collectionStateNotifierProvider
|
final requestBodyContentType = ref.watch(collectionStateNotifierProvider
|
||||||
.select((value) => value[idIdx].requestBodyContentType));
|
.select((value) => value[idIdx].requestBodyContentType));
|
||||||
return DropdownButton<ContentType>(
|
return DropdownButton<ContentType>(
|
||||||
focusColor: kColorGrey50,
|
focusColor: surfaceColor,
|
||||||
value: requestBodyContentType,
|
value: requestBodyContentType,
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.unfold_more_rounded,
|
Icons.unfold_more_rounded,
|
||||||
@ -95,55 +112,3 @@ class _DropdownButtonBodyContentTypeState
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TextFieldEditor extends ConsumerStatefulWidget {
|
|
||||||
const TextFieldEditor({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
ConsumerState<TextFieldEditor> createState() => _TextFieldEditorState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _TextFieldEditorState extends ConsumerState<TextFieldEditor> {
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final activeId = ref.watch(activeIdStateProvider);
|
|
||||||
final reqestModel = ref
|
|
||||||
.read(collectionStateNotifierProvider.notifier)
|
|
||||||
.getRequestModel(activeId!);
|
|
||||||
return TextFormField(
|
|
||||||
key: Key("$activeId-body"),
|
|
||||||
keyboardType: TextInputType.multiline,
|
|
||||||
expands: true,
|
|
||||||
maxLines: null,
|
|
||||||
textAlignVertical: TextAlignVertical.top,
|
|
||||||
initialValue: reqestModel.requestBody ?? "",
|
|
||||||
style: kCodeStyle,
|
|
||||||
onChanged: (value) {
|
|
||||||
ref
|
|
||||||
.read(collectionStateNotifierProvider.notifier)
|
|
||||||
.update(activeId, requestBody: value);
|
|
||||||
},
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: "Enter content (body)",
|
|
||||||
hintStyle: kCodeHintStyle,
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderRadius: kBorder12,
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderRadius: kBorder12,
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: Theme.of(context).colorScheme.surfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
103
lib/widgets/editor.dart
Normal file
103
lib/widgets/editor.dart
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:apidash/consts.dart';
|
||||||
|
|
||||||
|
class TextFieldEditor extends StatefulWidget {
|
||||||
|
const TextFieldEditor(
|
||||||
|
{Key? key, required this.fieldKey, this.onChanged, this.initialValue})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
final String fieldKey;
|
||||||
|
final Function(String)? onChanged;
|
||||||
|
final String? initialValue;
|
||||||
|
@override
|
||||||
|
State<TextFieldEditor> createState() => _TextFieldEditorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TextFieldEditorState extends State<TextFieldEditor> {
|
||||||
|
final TextEditingController controller = TextEditingController();
|
||||||
|
final editorFocusNode = FocusNode();
|
||||||
|
final keyboardListnerFocusNode = FocusNode();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertTab() {
|
||||||
|
String sp = " ";
|
||||||
|
int offset = math.min(
|
||||||
|
controller.selection.baseOffset, controller.selection.extentOffset);
|
||||||
|
String text = controller.text.substring(0, offset) +
|
||||||
|
sp +
|
||||||
|
controller.text.substring(offset);
|
||||||
|
controller.value = TextEditingValue(
|
||||||
|
text: text,
|
||||||
|
selection: controller.selection.copyWith(
|
||||||
|
baseOffset: controller.selection.baseOffset + sp.length,
|
||||||
|
extentOffset: controller.selection.extentOffset + sp.length,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if (widget.initialValue != null) {
|
||||||
|
controller.text = widget.initialValue!;
|
||||||
|
}
|
||||||
|
return RawKeyboardListener(
|
||||||
|
focusNode: keyboardListnerFocusNode,
|
||||||
|
onKey: (RawKeyEvent event) {
|
||||||
|
if (event.runtimeType == RawKeyDownEvent &&
|
||||||
|
(event.logicalKey == LogicalKeyboardKey.tab)) {
|
||||||
|
if (kIsWeb) {
|
||||||
|
FocusScope.of(context).previousFocus();
|
||||||
|
} else {
|
||||||
|
editorFocusNode.requestFocus();
|
||||||
|
}
|
||||||
|
insertTab();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: TextFormField(
|
||||||
|
key: Key(widget.fieldKey),
|
||||||
|
controller: controller,
|
||||||
|
focusNode: editorFocusNode,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
expands: true,
|
||||||
|
maxLines: null,
|
||||||
|
style: kCodeStyle,
|
||||||
|
textAlignVertical: TextAlignVertical.top,
|
||||||
|
onChanged: widget.onChanged,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: "Enter content (body)",
|
||||||
|
hintStyle: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.outline.withOpacity(
|
||||||
|
kHintOpacity,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
focusedBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Theme.of(context).colorScheme.primary.withOpacity(
|
||||||
|
kHintOpacity,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
enabledBorder: OutlineInputBorder(
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: Theme.of(context).colorScheme.surfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
keyboardListnerFocusNode.dispose();
|
||||||
|
editorFocusNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user