Files
MookNote/lib/widgets/text_input_panel.dart
2026-06-29 01:13:52 +08:00

144 lines
5.0 KiB
Dart

import 'package:flutter/material.dart';
/// 右侧滑入文本输入弹窗(单行/多行编辑用,如影视名称、书籍名称、简介)
class TextInputPanel extends StatefulWidget {
final String title;
final String initialValue;
final String hint;
final TextInputType keyboardType;
final int maxLines;
const TextInputPanel({
super.key,
required this.title,
this.initialValue = '',
this.hint = '',
this.keyboardType = TextInputType.text,
this.maxLines = 1,
});
static Future<String?> show({
required BuildContext context,
required String title,
String initialValue = '',
String hint = '',
TextInputType keyboardType = TextInputType.text,
int maxLines = 1,
}) {
return showGeneralDialog<String>(
context: context,
barrierDismissible: true,
barrierLabel: 'text-input-panel',
barrierColor: Colors.black.withValues(alpha: 0.3),
transitionDuration: const Duration(milliseconds: 250),
pageBuilder: (_, __, ___) => const SizedBox.shrink(),
transitionBuilder: (ctx, anim, secAnim, child) {
return SlideTransition(
position: Tween(begin: const Offset(1, 0), end: Offset.zero)
.animate(CurvedAnimation(parent: anim, curve: Curves.easeOut)),
child: Align(
alignment: Alignment.centerRight,
child: TextInputPanel(
title: title,
initialValue: initialValue,
hint: hint,
keyboardType: keyboardType,
maxLines: maxLines,
),
),
);
},
);
}
@override
State<TextInputPanel> createState() => _TextInputPanelState();
}
class _TextInputPanelState extends State<TextInputPanel> {
late final TextEditingController _controller;
@override
void initState() {
super.initState();
_controller = TextEditingController(text: widget.initialValue);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _submit() {
final value = _controller.text.trim();
Navigator.pop(context, value);
}
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
final screenWidth = MediaQuery.of(context).size.width;
return Material(
color: colors.surface,
borderRadius: const BorderRadius.horizontal(left: Radius.circular(16)),
clipBehavior: Clip.antiAlias,
child: SizedBox(
width: screenWidth * 0.75,
height: double.infinity,
child: Column(
children: [
// Header
Container(
padding: EdgeInsets.fromLTRB(16, MediaQuery.of(context).padding.top + 12, 8, 12),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: colors.outlineVariant, width: 0.5)),
),
child: Row(
children: [
Text(widget.title, style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: colors.onSurface)),
const Spacer(),
TextButton(
onPressed: _submit,
child: Text('完成', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: colors.primary)),
),
],
),
),
// 输入框
Padding(
padding: const EdgeInsets.all(16),
child: TextField(
controller: _controller,
autofocus: true,
keyboardType: widget.maxLines > 1 ? TextInputType.multiline : widget.keyboardType,
maxLines: widget.maxLines,
style: TextStyle(fontSize: 15, color: colors.onSurface),
cursorColor: colors.primary,
decoration: InputDecoration(
hintText: widget.hint,
hintStyle: TextStyle(fontSize: 15, color: colors.onSurface.withValues(alpha: 0.3)),
filled: true,
fillColor: colors.surfaceContainerHigh,
contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide.none),
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide.none),
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: colors.primary, width: 1)),
suffixIcon: _controller.text.isNotEmpty
? IconButton(
icon: Icon(Icons.clear, size: 18, color: colors.onSurface.withValues(alpha: 0.35)),
onPressed: () => _controller.clear(),
)
: null,
),
onChanged: (_) => setState(() {}),
onSubmitted: (_) => _submit(),
),
),
],
),
),
);
}
}