马良AI写作初始化仓库
This commit is contained in:
127
AINoval/lib/components/editable_title.dart
Normal file
127
AINoval/lib/components/editable_title.dart
Normal file
@@ -0,0 +1,127 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class Debouncer {
|
||||
|
||||
Debouncer({this.delay = const Duration(milliseconds: 500)});
|
||||
Timer? _timer;
|
||||
final Duration delay;
|
||||
|
||||
void run(Function() action) {
|
||||
_timer?.cancel();
|
||||
_timer = Timer(delay, action);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_timer?.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
class EditableTitle extends StatefulWidget {
|
||||
|
||||
const EditableTitle({
|
||||
Key? key,
|
||||
required this.initialText,
|
||||
this.onChanged,
|
||||
this.onSubmitted,
|
||||
this.commitOnBlur = true,
|
||||
this.style,
|
||||
this.textAlign = TextAlign.left,
|
||||
this.autofocus = false,
|
||||
}) : super(key: key);
|
||||
final String initialText;
|
||||
// 可选:仅用于本地UI联动(不做持久化)
|
||||
final Function(String)? onChanged;
|
||||
// 提交时回调:回车或失焦触发
|
||||
final Function(String)? onSubmitted;
|
||||
// 失焦时是否提交
|
||||
final bool commitOnBlur;
|
||||
final TextStyle? style;
|
||||
final TextAlign textAlign;
|
||||
final bool autofocus;
|
||||
|
||||
@override
|
||||
State<EditableTitle> createState() => _EditableTitleState();
|
||||
}
|
||||
|
||||
class _EditableTitleState extends State<EditableTitle> {
|
||||
late TextEditingController _controller;
|
||||
late Debouncer _debouncer;
|
||||
late FocusNode _focusNode;
|
||||
String _lastCommittedText = '';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = TextEditingController(text: widget.initialText);
|
||||
_debouncer = Debouncer();
|
||||
_focusNode = FocusNode();
|
||||
_lastCommittedText = widget.initialText;
|
||||
|
||||
_focusNode.addListener(() {
|
||||
if (!_focusNode.hasFocus && widget.commitOnBlur) {
|
||||
_commitIfChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(EditableTitle oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.initialText != widget.initialText) {
|
||||
_controller.text = widget.initialText;
|
||||
// 外部更新时同步已提交文本基线
|
||||
_lastCommittedText = widget.initialText;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_debouncer.dispose();
|
||||
_controller.dispose();
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _commitIfChanged() {
|
||||
final current = _controller.text;
|
||||
if (current != _lastCommittedText) {
|
||||
_lastCommittedText = current;
|
||||
if (widget.onSubmitted != null) {
|
||||
widget.onSubmitted!(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: TextField(
|
||||
controller: _controller,
|
||||
focusNode: _focusNode,
|
||||
style: widget.style,
|
||||
decoration: const InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
isDense: true,
|
||||
),
|
||||
textAlign: widget.textAlign,
|
||||
autofocus: widget.autofocus,
|
||||
// onChanged 仅用于本地UI联动(不持久化)
|
||||
onChanged: (value) {
|
||||
if (widget.onChanged != null) {
|
||||
_debouncer.run(() {
|
||||
widget.onChanged!(value);
|
||||
});
|
||||
}
|
||||
},
|
||||
// 按下回车时提交
|
||||
onSubmitted: (_) {
|
||||
_commitIfChanged();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user