简体   繁体   English

Flutter overlayEntry 在 state 更改后不更新内容

[英]Flutter overlayEntry doesn't update content after state change

I'm trying to implement an overlay function for a text field that shows some particular state depending on what's the user input.我正在尝试为显示某些特定 state 的文本字段实现覆盖 function,具体取决于用户输入的内容。 When the user taps the text field, it will display an overlay that should show some information while the user is writing, much like a search bar on a web browser works.当用户点击文本字段时,它会显示一个叠加层,在用户书写时应该显示一些信息,就像 web 浏览器上的搜索栏一样。

My problem is that the content of the OverlayEntry doesn't update at the same time the user input change, it only changes when I reopen the Overlay.我的问题是 OverlayEntry 的内容不会在用户输入更改的同时更新,它只会在我重新打开 Overlay 时更改。

Expected behavior预期行为

在此处输入图像描述

Current behaviour当前行为

在此处输入图像描述

For testing this functionality, I'm just passing the same value from the text field to the overlay.为了测试此功能,我只是将相同的值从文本字段传递到叠加层。

This is my code:这是我的代码:

Home class主页 class

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
        onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
        child: Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Center(
            child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  CustomTextField(),
                ]),
            // This trailing comma makes auto-formatting nicer for build methods.
          ),
        ));
  }
}

Custom Text field class:自定义文本字段 class:

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

  @override
  State<CustomTextField> createState() => _CustomTextFieldState();
}

class _CustomTextFieldState extends State<CustomTextField>
    with TickerProviderStateMixin {
  TextEditingController controlador = TextEditingController();
  FocusNode nodeUno = FocusNode();
  OverlayEntry? _overlayEntry;
  GlobalKey globalKey = GlobalKey();
  final LayerLink _layerLink = LayerLink();
  String inputText = '';

  @override
  void initState() {
    super.initState();
    OverlayState? overlayState = Overlay.of(context);
    WidgetsBinding.instance!.addPostFrameCallback((_) {
      globalKey;
    });

    nodeUno.addListener(() {
      if (nodeUno.hasFocus) {
        _overlayEntry = _createOverlay(inputText);

        overlayState!.insert(_overlayEntry!);
      } else {
        _overlayEntry!.remove();
      }
    });
  }

  OverlayEntry _createOverlay([String? text]) {
    RenderBox renderBox = context.findRenderObject() as RenderBox;

    var size = renderBox.size;
    return OverlayEntry(
        builder: (context) => Positioned(
              width: size.width,
              child: CompositedTransformFollower(
                link: _layerLink,
                showWhenUnlinked: false,
                offset: Offset(0.0, size.height + 5.0),
                child: Material(
                  elevation: 5.0,
                  child: Column(
                    children: [
                      ListTile(
                        title: text != '' ? Text(text!) : const Text('data'),
                      ),
                    ],
                  ),
                ),
              ),
            ));
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 250,
      child: CompositedTransformTarget(
        link: _layerLink,
        child: TextField(
          focusNode: nodeUno,
          controller: controlador,
          onChanged: (value) {
            print(value);
            setState(() {
              inputText = value;
            });
          },
        ),
      ),
    );
  }
}

Well, for what I could find, the only way to update the content of an overlay entry is to remove and insert again that entry, so this is what I found that works:好吧,据我所知,更新覆盖条目内容的唯一方法是删除并再次插入该条目,所以这就是我发现的有效方法:

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

  @override
  State<CustomTextField> createState() => _CustomTextFieldState();
}

class _CustomTextFieldState extends State<CustomTextField>
    with TickerProviderStateMixin {
  TextEditingController controlador = TextEditingController();
  FocusNode nodeUno = FocusNode();
  OverlayEntry? _overlayEntry;
  GlobalKey globalKey = GlobalKey();
  final LayerLink _layerLink = LayerLink();
  String inputText = '';
  OverlayState? overlayState;
  bool popoverOpened = false;

  @override
  void initState() {
    super.initState();
    _overlayEntry = null;
    overlayState = Overlay.of(context);
    WidgetsBinding.instance!.addPostFrameCallback((_) {
      globalKey;
    });

    nodeUno.addListener(() {
      if (nodeUno.hasFocus) {
        _overlayEntry = _createOverlay(inputText);
        overlayState!.insert(_overlayEntry!);
      } else {
        if (_overlayEntry != null) {
          _overlayEntry?.remove();
          _overlayEntry = null;
        }
      }
    });
  }

  OverlayEntry _createOverlay([String? text]) {
    RenderBox renderBox = context.findRenderObject() as RenderBox;

    if (_overlayEntry != null && popoverOpened) {
      _overlayEntry!.remove();
    }
    popoverOpened = true;
    var size = renderBox.size;
    return OverlayEntry(
        builder: (context) => Positioned(
              width: size.width,
              child: CompositedTransformFollower(
                link: _layerLink,
                showWhenUnlinked: false,
                offset: Offset(0.0, size.height + 5.0),
                child: OverlayClass(val: inputText),
              ),
            ));
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 250,
      child: CompositedTransformTarget(
        link: _layerLink,
        child: TextField(
          focusNode: nodeUno,
          controller: controlador,
          onChanged: (value) {
            print(value);
            setState(() {
              inputText = value;
              _overlayEntry = _createOverlay(inputText);
              overlayState!.insert(_overlayEntry!);
            });
          },
        ),
      ),
    );
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM