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

如何在Jetpack Compose中使用AnimatedContent切换TextField与Text并隐藏过渡占位符

解决方案

问题根源

用text != null作为targetState时,TextField在退出动画的短暂时间内仍会保持渲染状态,导致placeholder“Search”意外显示;直接用text作为targetState无效,通常是因为状态更新未正确触发组件重组,或是transitionBuilder的逻辑未适配状态变化。

实现无占位符的平滑过渡

以下是两种可行的实现方式:

方式一:绑定Placeholder到当前状态

直接将TextField的hintText与当前状态绑定,确保退出动画阶段placeholder自动隐藏:

String? _text;

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Center(
      child: AnimatedContent(
        // 使用文本内容本身作为targetState,确保状态变化触发过渡
        targetState: _text,
        transitionBuilder: (child, animation) {
          return FadeTransition(
            opacity: animation,
            child: SizeTransition(
              sizeFactor: animation,
              axis: Axis.horizontal,
              child: child,
            ),
          );
        },
        builder: (context, currentText) {
          if (currentText != null) {
            return TextField(
              controller: TextEditingController(text: currentText),
              decoration: InputDecoration(
                // 仅当当前状态非空时显示placeholder
                hintText: _text != null ? 'Search' : null,
                border: InputBorder.none,
              ),
              onChanged: (value) => setState(() => _text = value),
            );
          } else {
            return const Text('点击开始搜索');
          }
        },
      ),
    ),
    floatingActionButton: IconButton(
      icon: const Icon(Icons.close),
      onPressed: () => setState(() => _text = null),
    ),
  );
}

方式二:利用动画状态动态隐藏TextField

在transitionBuilder中监听动画进度,当处于退出动画时直接隐藏TextField,避免placeholder显示:

AnimatedContent(
  targetState: _text,
  transitionBuilder: (child, animation) {
    return AnimatedBuilder(
      animation: animation,
      builder: (context, child) {
        // 动画值小于0.5时判定为退出阶段
        final isExiting = animation.value < 0.5;
        return FadeTransition(
          opacity: animation,
          child: isExiting ? const SizedBox.shrink() : child,
        );
      },
      child: child,
    );
  },
  builder: (context, currentText) {
    if (currentText != null) {
      return TextField(
        controller: TextEditingController(text: currentText),
        decoration: const InputDecoration(
          hintText: 'Search',
          border: InputBorder.none,
        ),
        onChanged: (value) => setState(() => _text = value),
      );
    } else {
      return const Text('点击开始搜索');
    }
  },
)

关键注意点

  • 必须使用text(文本内容本身)作为targetState,而非text != null的布尔值,这样AnimatedContent才能感知到状态的具体变化并触发正确过渡。
  • 确保通过setState更新状态,触发组件重组,否则AnimatedContent不会响应状态变化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 05:32:40