Flutter之输入组件 2025-07-22 周二

组件选择

  • 有iOS风格的CupertinoTextField和Android风格的TextField两种;
  • 从外观来看,还是CupertinoTextField好一些;
  • CupertinoTextField还有自动出现的删除按钮,非常实用;
  • 一开始我们选择的是CupertinoTextField,后来据说遇到三星手机输入框出现自动关闭的问题,所以我们换成了TextField;

封装想法

  • 我们不需要外边框和错误提示信息什么的,所以外部套一个Container
  • Container相关的margin,padding,宽,高,背景色,圆角等参数可以放出来,或者给个默认值。
  • TextField专门做输入,前面,后面的图标等都不用,用一个Row组件套外层,可以完全自定义。
  • TextField需要指定宽度,放在Row中,只要套一个Expanded组件就可以了。
  • 文本样式,占位符及其样式,用的比较多,给个默认值;
  • 通过监听FocusNode的方式实现删除按钮功能;
  • 至于提交,变换,键盘类型等各种输入相关参数,直接透传就可以了

参考代码

class OneLineInputWidget extends StatefulWidget {
  const OneLineInputWidget({
    super.key,
    this.placeholder,
    this.placeholderStyle,
    this.style,
    this.controller,
    this.onSubmitted,
    this.textInputAction,
    this.keyboardType,
    this.textAlign = TextAlign.start,
    this.enabled = true,
    this.obscureText = false,
    this.width,
    this.radius = 0,
    this.margin,
    this.padding,
    this.bgColor,
    this.height,
    this.focusNode,
    this.showCursor = false,
    this.defaultText,
    this.onChanged,
    this.inputFormatters,
  });

  final String? placeholder;
  final TextStyle? placeholderStyle;
  final TextStyle? style;
  final TextEditingController? controller;
  final ValueChanged<String>? onSubmitted;
  final ValueChanged<String>? onChanged;
  final TextInputAction? textInputAction;
  final TextInputType? keyboardType;
  final TextAlign textAlign;
  final bool enabled;
  final bool obscureText;
  final double? width;
  final double radius;
  final EdgeInsetsGeometry? margin;
  final EdgeInsetsGeometry? padding;
  final Color? bgColor;
  final double? height;
  final FocusNode? focusNode;
  final bool showCursor;
  final String? defaultText;
  final List<TextInputFormatter>? inputFormatters;

  @override
  State<OneLineInputWidget> createState() => _OneLineInputWidgetState();
}

class _OneLineInputWidgetState extends State<OneLineInputWidget> {
  late FocusNode actualFocusNode;
  late TextEditingController actualController;
  bool isFocused = false;

  @override
  void initState() {
    super.initState();

    actualFocusNode = widget.focusNode ?? FocusNode();
    actualController = widget.controller ?? TextEditingController();

    if (widget.showCursor) {
      Future.delayed(Duration.zero, () {
        FocusScope.of(Get.context!).requestFocus(actualFocusNode);
      });
    }
    if ((widget.defaultText != null) && (widget.defaultText!.isNotEmpty)) {
      actualController.text = widget.defaultText!;
    }

    /// 监听焦点变化
    actualFocusNode.addListener(() {
      if (actualFocusNode.hasFocus) {
        isFocused = true;
      } else {
        isFocused = false;
      }
      setState(() {});
    });
  }

  @override
  void dispose() {
    if (widget.controller == null) {
      actualController.dispose();
    }
    if (widget.focusNode == null) {
      actualFocusNode.dispose();
    }
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    bool showSuffix = widget.enabled && isFocused;
    return Container(
      width: widget.width,
      height: widget.height ?? 40.r,
      margin: widget.margin,
      padding: widget.padding,
      decoration: BoxDecoration(
        color: widget.bgColor,
        borderRadius: BorderRadius.circular(widget.radius),
      ),
      child: Row(
        children: [
          Expanded(
            child: TextField(
              controller: actualController,
              decoration: InputDecoration(
                hintText: widget.placeholder ?? "please_input".tr,
                hintStyle: widget.placeholderStyle ?? StyleUtils.ts_99_12_400,
                border: InputBorder.none,
                suffixIcon: showSuffix
                    ? GestureDetector(
                        onTap: () {
                          actualController.clear();
                        },
                        child: Icon(
                          Icons.clear_outlined,
                          color: ColorUtils.c_9a9a9a,
                        ),
                      )
                    : null,
              ),
              style: widget.style ?? StyleUtils.ts_33_13_500,
              //键盘回车搜索
              textInputAction: widget.textInputAction,
              onSubmitted: widget.onSubmitted,
              onChanged: widget.onChanged,
              keyboardType: widget.keyboardType,
              textAlign: widget.textAlign,
              enabled: widget.enabled,
              obscureText: widget.obscureText,
              focusNode: actualFocusNode,
              inputFormatters: widget.inputFormatters,
            ),
          ),
        ],
      ),
    );
  }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容