重新加载颜色容器以空擦除 Flutter 中的 TextField

Reload color container to null erase TextField inside Flutter

我有一个 ContainerStack 内包装了一个 CupertinoTextField

当我在文本字段上完成书写时,我按下一个按钮并使用 FocusNode 清除焦点,然后重新加载 UI。

对于一个用例,我想将颜色容器重新加载为 null,但是当我重新加载时,文本字段似乎 'erased'。然后,当我再次点击 textfield 时,某些东西挡住了 textfield 。只有在第二次点击时,我才能检索文本字段焦点。

这是我上面描述的预览:

这是完整的代码:

class EditorTextPage extends StatefulWidget {
  const EditorTextPage({Key? key}) : super(key: key);

  @override
  _EditorTextPageState createState() => _EditorTextPageState();
}

class _EditorTextPageState extends State<EditorTextPage> {
  FocusNode focus = FocusNode();
  bool displayEditor = true;

  @override
  void initState() {
    focus.addListener(() {
      if (focus.hasFocus) {
        setState(() {
          displayEditor = true;
        });
      }
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      color: displayEditor ? Colors.red : null,
      child: SafeArea(
        bottom: false,
        child: Stack(
          children: [
            Positioned(
              top: 100,
              left: 200,
              child: IntrinsicWidth(
                child: CupertinoTextField(
                  focusNode: focus,
                  minLines: 1,
                  maxLines: 3,
                  padding: EdgeInsets.all(10),
                  cursorColor: Colors.white,
                  decoration: BoxDecoration(color: Colors.lightBlueAccent),
                ),
              ),
            ),
            Align(
                alignment: Alignment.bottomCenter,
                child: Container(
                  margin: EdgeInsets.only(bottom: 50),
                  child: CupertinoButton(
                    color: Colors.black,
                    onPressed: () {
                      setState(() {
                        if (focus.hasFocus) {
                          focus.unfocus();
                          displayEditor = false;
                        }
                      });
                    },
                    child: Text(
                      "DONE",
                    ),
                  ),
                )),
          ],
        ),
      ),
    );
  }
}

为什么会这样?以及如何修复它?

问题是什么

Container 使用 ColoredBox 来显示颜色。然而 ColoredBox 必须 有颜色,所以实际上 Container 使用 ColoredBox 只有 if color!=null.

这会为您提供以下小部件树:

  • color!=null[Container -> ColoredBox -> Stack]
  • color==null[Container -> Stack]

现在如果你知道 flutter state 是如何工作的,你就会知道如果同一级别的 widget 树不发生变化,一个 state 会重新附加到同一个 widget。在这里,由于 CupertinoTextField 上方的小部件树发生了变化(ColoredBoxStack 替换,反之亦然),当颜色变为 null 或不再为 null 时,状态无法正确重建。

下面是详细情况

  1. 第一次聚焦时 TextField 你的颜色不为空,小部件树没有改变,一切正常。
  2. 当您失去焦点时,小部件树会被修改,因此您的 TextField 状态会丢失 => 文本为“重置”
  3. 当您重新聚焦时,小部件树再次发生变化,因此您的 TextField 再次发生变化,导致键盘关闭
  4. 你回到了 1

有关 Flutter 键的更多信息,请参阅:When to Use Keys

如何解决

关键(:o)是让flutter明白Container下面的StackColoredBox下面的是一样的。为此,请为其分配一个 GlobalKey(如果您不明白为什么,请参阅我给您的视频)。

实现如下:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(home: EditorTextPage()));
}

class EditorTextPage extends StatefulWidget {
  const EditorTextPage({Key? key}) : super(key: key);

  @override
  _EditorTextPageState createState() => _EditorTextPageState();
}

class _EditorTextPageState extends State<EditorTextPage> {
  final focus = FocusNode();
  bool displayEditor = true;

  final _key = GlobalKey();

  @override
  void initState() {
    focus.addListener(() {
      if (focus.hasFocus) {
        setState(() {
          focus.requestFocus();
          displayEditor = true;
        });
      }
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        color: displayEditor ? Colors.red : null,
        child: SafeArea(
          bottom: false,
          child: Stack(
            key: _key,
            children: [
              Positioned(
                top: 100,
                left: 200,
                child: IntrinsicWidth(
                  child: CupertinoTextField(
                    focusNode: focus,
                    minLines: 1,
                    maxLines: 3,
                    padding: EdgeInsets.all(10),
                    cursorColor: Colors.white,
                    decoration: BoxDecoration(color: Colors.lightBlueAccent),
                  ),
                ),
              ),
              Align(
                alignment: Alignment.bottomCenter,
                child: Container(
                  margin: EdgeInsets.only(bottom: 50),
                  child: CupertinoButton(
                    color: Colors.black,
                    onPressed: () {
                      if (focus.hasFocus) {
                        setState(() {
                          focus.unfocus();
                          displayEditor = false;
                        });
                      }
                    },
                    child: Text(
                      "DONE",
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

感谢您的挑战,这真的很有趣!

想要另一个没有密钥的修复程序吗?

另一种选择是避免完全修改小部件树。因此,解决方案是颜色永远不会为空,例如将其设置为透明:

// GOOD
color: displayEditor ? Colors.red : Colors.transparent

// BAD
color: displayEditor ? Colors.red : null

用什么?

我不知道。

第一个选项挑战flutter树算法

第二个选项是渲染透明色是否对flutter有挑战,这个我不知道也找不到。

有人认为这会很有趣!