You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter模态框输入框变量未实时更新:发送图标颜色无变化

Flutter发送图标颜色不实时更新问题解决

问题

项目中buildInput方法内,通过_isInputEmpty ? Colors.grey : Colors.black控制的发送图标颜色,在输入或清空文本框时无法实时变化,必须退出modalSheet并重新进入才能看到颜色更新。

原因

showModalBottomSheet会创建独立的路由上下文,父组件的setState只能触发自身UI重建,无法影响modalSheet内部的组件刷新。虽然_handleTextChange里调用了父组件的setState更新_isInputEmpty,但modalSheet中的buildInput不会随之重新构建,导致颜色无法实时更新。

解决方案

方案一:将输入区域封装为独立StatefulWidget

把输入相关的状态(控制器、_isInputEmpty)放到单独的StatefulWidget中,让输入组件自行管理状态,内部的setState可直接触发自身UI更新。

示例代码:

class CommentInput extends StatefulWidget {
  final VoidCallback onSend;
  final TextEditingController? controller;
  final FocusNode? focusNode;

  const CommentInput({
    super.key,
    required this.onSend,
    this.controller,
    this.focusNode,
  });

  @override
  State<CommentInput> createState() => _CommentInputState();
}

class _CommentInputState extends State<CommentInput> {
  late TextEditingController _controller;
  late FocusNode _focusNode;
  bool _isInputEmpty = true;

  @override
  void initState() {
    super.initState();
    _controller = widget.controller ?? TextEditingController();
    _focusNode = widget.focusNode ?? FocusNode();
    _controller.addListener(_handleTextChange);
    _isInputEmpty = _controller.text.isEmpty;
  }

  void _handleTextChange() {
    if (mounted) {
      setState(() {
        _isInputEmpty = _controller.text.isEmpty;
      });
    }
  }

  @override
  void dispose() {
    if (widget.controller == null) _controller.dispose();
    else _controller.removeListener(_handleTextChange);
    
    if (widget.focusNode == null) _focusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    double width = MediaQuery.of(context).size.width;
    double height = MediaQuery.of(context).size.height;

    return Row(
      children: [
        Expanded(
          child: Container(
            height: height * .043,
            decoration: BoxDecoration(
              color: kBackgroundColor,
              borderRadius: BorderRadius.circular(40),
            ),
            child: Row(
              children: [
                Expanded(
                  child: TextInput(
                    keyBoardType: TextInputType.multiline,
                    controller: _controller,
                    onTapOutside: false,
                    focusNode: _focusNode,
                    onFocus: () {
                      // 若需处理焦点逻辑可在此添加
                    },
                    hintText: "Add a comment",
                  ),
                ),
                SizedBox(width: width * 0.03),
                GestureDetector(
                  onTap: () {
                    String text = _controller.text.trimRight();
                    if (text.isEmpty) return;
                    _controller.text = '';
                    widget.onSend();
                  },
                  child: Container(
                    height: height * .043,
                    width: 35,
                    decoration: BoxDecoration(
                      color: _isInputEmpty ? const Color(0XFF333333) : Colors.white,
                      borderRadius: const BorderRadius.all(Radius.circular(8)),
                    ),
                    child: Center(
                      child: Icon(
                        Icons.send,
                        color: _isInputEmpty ? Colors.grey : Colors.black,
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ],
    );
  }
}

在showDraggableSheet中替换原buildInput(context)为:

CommentInput(
  onSend: () {
    String text = myController.text.trimRight();
    if (text.isNotEmpty) {
      // 这里添加提交评论的逻辑
      myController.text = '';
    }
  },
  controller: myController,
  focusNode: _focusNode,
)

方案二:使用ValueNotifier监听状态变化

用ValueNotifier<bool>管理_isInputEmpty状态,在modalSheet内部通过ValueListenableBuilder监听变化,实时更新UI。

修改父组件的状态定义:

final myController = TextEditingController();
final ValueNotifier<bool> _isInputEmpty = ValueNotifier(true);

@override
void initState() {
  super.initState();
  fetchStreamingLinks();
  getComments();
  myController.addListener(_handleTextChange);
}

void _handleTextChange() {
  _isInputEmpty.value = myController.text.isEmpty;
}

修改buildInput方法:

Widget buildInput(BuildContext context) {
  double width = MediaQuery.of(context).size.width;
  double height = MediaQuery.of(context).size.height;

  return Row(
    children: [
      Expanded(
        child: Container(
          height: height * .043,
          decoration: BoxDecoration(
            color: kBackgroundColor,
            borderRadius: BorderRadius.circular(40),
          ),
          child: Row(
            children: [
              Expanded(
                child: TextInput(
                  keyBoardType: TextInputType.multiline,
                  controller: myController,
                  onTapOutside: false,
                  focusNode: _focusNode,
                  onFocus: () {
                    setState(() {
                      inputFocus = true;
                    });
                  },
                  hintText: "Add a comment",
                ),
              ),
              SizedBox(width: width * 0.03),
              ValueListenableBuilder<bool>(
                valueListenable: _isInputEmpty,
                builder: (context, isEmpty, child) {
                  return GestureDetector(
                    onTap: () {
                      String text = myController.text.trimRight();
                      if (text.isEmpty) return;
                      myController.text = '';
                    },
                    child: Container(
                      height: height * .043,
                      width: 35,
                      decoration: BoxDecoration(
                        color: isEmpty ? const Color(0XFF333333) : Colors.white,
                        borderRadius: const BorderRadius.all(Radius.circular(8)),
                      ),
                      child: Center(
                        child: Icon(
                          Icons.send,
                          color: isEmpty ? Colors.grey : Colors.black,
                        ),
                      ),
                    ),
                  );
                },
              ),
            ],
          ),
        ),
      ),
    ],
  );
}

_isInputEmpty的变化会通过ValueNotifier通知ValueListenableBuilder,实时重建发送按钮区域,更新颜色。

内容的提问来源于stack exchange,提问作者Fornow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 02:15:54