首页 > 解决方案 > Flutter Newbie:修改 Textfield 值打破对 TextField 的关注

问题描述

放轻松。我一周前才开始学习 Flutter。我来自 ReactJS,所以我对状态管理和生命周期方法有很好的理解。但我对 Dart 和 Flutter 以及它如何处理状态完全陌生。

我正在编写一个快速的 WebRTC 聊天应用程序。我有一个用来生成房间名称的 TextField。我决定要制作 TextField 的 labelText,每 5 秒循环一些随机单词,而该字段不在焦点上。如果该领域成为焦点,我停止循环标签。我这样做是为了使该字段看起来有一个预先生成的随机房间名称。

我在编辑 TextField 时遇到问题。我认为这是 setState 或我的 TextEditingController 的问题。我习惯于能够访问输入的值,所以控制器对我来说很奇怪。

这是我的更改文本字段:

import 'dart:async';

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

//

class ChangingTextField extends StatefulWidget {
  final TextEditingController controller;

  ChangingTextField({
    Key? key,
    required this.controller,
  }) : super(key: key);

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


class _ChangingTextFieldState extends State<ChangingTextField> {
  FocusNode _focusNode = FocusNode();
  Timer? _timer;
  String _roomName = "example.com/";
  bool _wasFocused = false;

  @override
  void initState() {
    super.initState();
    _focusNode = FocusNode();
    _timer = Timer.periodic(Duration(seconds: 5), (Timer t) => _genRoomName());
  }

  @override
  void dispose() {
    _timer?.cancel();
    _focusNode.dispose();
    super.dispose();
  }

  void _requestFocus(){
    if(!_wasFocused){
      setState(() {
        _timer?.cancel();
        _wasFocused = true;
        FocusScope.of(context).requestFocus(_focusNode);
      });
    }
  }

  void _genRoomName(){
    WordPair wp = generateWordPairs().take(1).first;
    setState(() => _roomName = "example.com/" + wp.first + "-" + wp.second );
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: TextField(
        focusNode: _focusNode,
        controller: widget.controller,
        decoration: InputDecoration(
            border: OutlineInputBorder(),
            labelText: _wasFocused ? "example.com/" : _roomName,
        ),
        onTap: _requestFocus,
      ),
    );
  }
}

父小部件只是将 TextEditingController 传递到此小部件中,以便我可以监听更改,并且(我假设)在稍后的时间点收集 TextField 的值。

侦听器在父小部件中定义如下:

  @override
  void initState() {
    roomNameController.addListener(() {
      setState(() {});
    });
    super.initState();
  }

但是,每次我尝试更改 TextField 的值时,在我键入每个字符之后,焦点都会在 ChangingTextField 小部件上中断,我必须在 TextField 内再次单击以键入我的下一个字符。我假设这个问题是因为侦听器在父小部件中调用 setState。

在 React 术语中,我将其称为重新渲染。如果父级重新渲染,子级会随之而去,因此应用程序会丢失它所拥有的关于用户在小部件树中工作的位置的知识。但是,我觉得控制器需要存在于父级中,这样,我可以在需要时(例如按下按钮)获取子级的值。提升状态等等。

有人可以向我解释这里发生了什么吗?

标签: flutterdartcontrollerstatetextfield

解决方案


我找到了解决方案。在小部件内部进行侦听而不是在父组件中初始化侦听器会产生您期望的行为。

简而言之,移动以下代码:

  @override
  void initState() {
    roomNameController.addListener(() {
      setState(() {});
    });
    super.initState();
  }

进入ChangingTextField 小部件的initState 而不是将其置于父级的initState 中,解决了这个问题。最重要的是,控制器仍然由父级创建,因此当按下提交按钮时,控制器的文本在父级中可用。


推荐阅读