2024-03-20 18:31:37 +08:00
|
|
|
import 'package:flutter/material.dart';
|
2024-03-21 14:09:49 +08:00
|
|
|
import 'package:flutter/services.dart';
|
2024-03-20 18:31:37 +08:00
|
|
|
import 'package:sk_base_mobile/app_theme.dart';
|
|
|
|
import 'package:sk_base_mobile/util/screen_adaper_util.dart';
|
|
|
|
|
2024-04-03 13:45:34 +08:00
|
|
|
class SkNumberInput<T> extends StatefulWidget {
|
2024-03-20 18:31:37 +08:00
|
|
|
final TextEditingController textController;
|
2024-04-01 17:35:34 +08:00
|
|
|
final Function(FocusNode)? onTap;
|
2024-03-26 11:35:39 +08:00
|
|
|
final Function(String)? onChanged;
|
2024-04-03 13:45:34 +08:00
|
|
|
final Function(dynamic)? onTapOutside;
|
|
|
|
final Function(dynamic)? validator;
|
|
|
|
|
2024-04-01 17:35:34 +08:00
|
|
|
final bool isDense;
|
|
|
|
final EdgeInsetsGeometry? contentPadding;
|
|
|
|
final bool autoFocus;
|
2024-03-20 18:31:37 +08:00
|
|
|
final bool isRequired;
|
|
|
|
final String labelText;
|
|
|
|
final String? hint;
|
2024-04-07 17:32:46 +08:00
|
|
|
final ValueChanged<int>? onFieldSubmitted;
|
|
|
|
const SkNumberInput(
|
2024-04-03 13:45:34 +08:00
|
|
|
{super.key,
|
|
|
|
required this.textController,
|
|
|
|
this.onTap,
|
|
|
|
this.onChanged,
|
|
|
|
this.hint,
|
|
|
|
this.onTapOutside,
|
|
|
|
this.isRequired = false,
|
|
|
|
this.labelText = '',
|
|
|
|
this.onFieldSubmitted,
|
|
|
|
this.autoFocus = false,
|
|
|
|
this.contentPadding,
|
|
|
|
this.isDense = false,
|
|
|
|
this.validator});
|
2024-04-01 17:35:34 +08:00
|
|
|
|
|
|
|
@override
|
2024-04-03 13:45:34 +08:00
|
|
|
State<SkNumberInput> createState() => _SkNumberInputState<T>();
|
2024-04-01 17:35:34 +08:00
|
|
|
}
|
|
|
|
|
2024-04-03 13:45:34 +08:00
|
|
|
class _SkNumberInputState<T> extends State<SkNumberInput> {
|
2024-04-01 17:35:34 +08:00
|
|
|
late FocusNode focusNode = FocusNode();
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
if (widget.autoFocus) {
|
|
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
|
|
focusNode.requestFocus();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-20 18:31:37 +08:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return TextFormField(
|
2024-04-01 17:35:34 +08:00
|
|
|
focusNode: focusNode,
|
|
|
|
onTap: () {
|
2024-04-02 16:22:21 +08:00
|
|
|
if (widget.onTap != null) {
|
2024-04-01 17:35:34 +08:00
|
|
|
widget.onTap!(focusNode);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
controller: widget.textController,
|
2024-04-03 13:45:34 +08:00
|
|
|
onFieldSubmitted: (event) {
|
|
|
|
if (widget.onTapOutside != null) {
|
|
|
|
dynamic value;
|
|
|
|
if (T == double) {
|
|
|
|
value = double.tryParse(widget.textController.text);
|
|
|
|
|
|
|
|
widget.onTapOutside!(value as T);
|
|
|
|
} else {
|
|
|
|
value = int.tryParse(widget.textController.text);
|
|
|
|
}
|
|
|
|
if (widget.validator != null && !widget.validator!(value)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
widget.onTapOutside!(value as T);
|
|
|
|
}
|
|
|
|
},
|
2024-03-20 18:31:37 +08:00
|
|
|
onTapOutside: (event) {
|
2024-04-03 13:45:34 +08:00
|
|
|
if (widget.onTapOutside != null) {
|
|
|
|
dynamic value;
|
|
|
|
if (T == double) {
|
|
|
|
value = double.tryParse(widget.textController.text);
|
|
|
|
|
|
|
|
widget.onTapOutside!(value as T);
|
|
|
|
} else {
|
|
|
|
value = int.tryParse(widget.textController.text);
|
|
|
|
}
|
|
|
|
if (widget.validator != null && !widget.validator!(value)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
widget.onTapOutside!(value as T);
|
|
|
|
FocusScope.of(context).unfocus();
|
|
|
|
}
|
2024-03-20 18:31:37 +08:00
|
|
|
},
|
2024-04-01 17:35:34 +08:00
|
|
|
onChanged: widget.onChanged ?? (_) {},
|
2024-03-21 14:09:49 +08:00
|
|
|
textAlign: TextAlign.center,
|
2024-03-20 18:31:37 +08:00
|
|
|
keyboardType: TextInputType.number,
|
2024-03-21 14:09:49 +08:00
|
|
|
inputFormatters: <TextInputFormatter>[
|
2024-03-26 11:35:39 +08:00
|
|
|
// 限制输入内容只能是数字和小数点不限制位数
|
|
|
|
FilteringTextInputFormatter.allow(RegExp(r'^\d+\.?\d{0,10}')),
|
2024-03-21 14:09:49 +08:00
|
|
|
],
|
2024-03-20 18:31:37 +08:00
|
|
|
decoration: InputDecoration(
|
2024-04-01 17:35:34 +08:00
|
|
|
contentPadding: widget.contentPadding,
|
|
|
|
isDense: widget.isDense,
|
2024-03-26 11:35:39 +08:00
|
|
|
// contentPadding: EdgeInsets.symmetric(vertical: ScreenAdaper.height(18)),
|
2024-03-20 18:31:37 +08:00
|
|
|
floatingLabelBehavior: FloatingLabelBehavior.always,
|
|
|
|
label: Row(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
|
|
children: [
|
2024-04-01 17:35:34 +08:00
|
|
|
if (widget.isRequired)
|
2024-03-21 14:09:49 +08:00
|
|
|
Text(
|
2024-03-20 18:31:37 +08:00
|
|
|
"*",
|
2024-03-21 14:09:49 +08:00
|
|
|
style: TextStyle(
|
2024-03-28 17:18:46 +08:00
|
|
|
color: Colors.red, fontSize: ScreenAdaper.height(30)),
|
2024-03-20 18:31:37 +08:00
|
|
|
),
|
|
|
|
Text(
|
2024-04-01 17:35:34 +08:00
|
|
|
widget.labelText,
|
2024-03-28 17:18:46 +08:00
|
|
|
style: TextStyle(fontSize: ScreenAdaper.height(30)),
|
2024-03-20 18:31:37 +08:00
|
|
|
),
|
|
|
|
]),
|
|
|
|
focusedBorder: OutlineInputBorder(
|
|
|
|
borderSide:
|
|
|
|
const BorderSide(color: AppTheme.primaryColorLight, width: 2),
|
|
|
|
borderRadius: BorderRadius.circular(ScreenAdaper.sp(15))),
|
2024-04-01 17:35:34 +08:00
|
|
|
hintText: widget.hint ?? '请输入',
|
2024-03-20 18:31:37 +08:00
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|