Как вызвать requestFocus для TextField, созданного во время выполнения? - PullRequest
0 голосов
/ 12 февраля 2020

Я пытаюсь вызвать метод requestFocus для TextField, который был создан во время выполнения, однако функция callFocus () работает, только если я установил задержку в 2 секунды, возможно, потому что TextField создается асинхронно. Вопрос в том, как вызвать requestFocus после того, как TextField готов?

appBar: AppBar(
  title: appBarTitle,
  centerTitle: true,
  actions: <Widget>[
    Center(
      child: IconButton(
        icon: actionIcon,
        onPressed: () {
          setState(() {
            if (this.actionIcon.icon == Icons.search) {
              this.actionIcon = Icon(Icons.navigate_next);
              this.appBarTitle = TextField(
                controller: editingController,
                focusNode: _searchInsideFocus,
                onChanged: (findNext) {
                  webView.findAllAsync(find: findNext);
                },
                style: TextStyle(
                  color: Colors.white,
                ),
                decoration: InputDecoration(
                    border: UnderlineInputBorder(
                        borderSide: BorderSide.none),
                    prefixIcon: Icon(Icons.search, color: Colors.white),
                    hintText: "Search...",
                    hintStyle: TextStyle(color: Colors.white)),
              ).;
            } else {
              webView.findNext(forward: true);
            }
          });
          callFocus();
        },
      ),
    ),
  )
Future<Null> callFocus() async {
  await new Future.delayed(const Duration(seconds : 2)); //only works if delayed
  FocusScope.of(context).requestFocus(_searchInsideFocus);
}

Ответы [ 2 ]

0 голосов
/ 13 февраля 2020

Я считаю, что фокус не переносится сразу, потому что после изменения состояния движок пытается восстановить фокус на активном перед изменением виджета состояния (т.е. IconButton). Если в новом состоянии виджет с фокусом пропал, следующий виджет со свойством autofocus true будет активным.

Такое поведение стоит использовать. Просто установите для свойства autofocus текстового поля значение true и удалите кнопку после нажатия:

import 'package:flutter/material.dart';

class LabPage extends StatefulWidget {
  @override
  _LabPageState createState() => _LabPageState();
}

class _LabPageState extends State<LabPage> {
  bool inSearch = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
          title: inSearch
              ? TextField(
                  autofocus: true,
                  onChanged: (findNext) {
                    // webView.findAllAsync(find: findNext);
                  },
                  style: TextStyle(
                    color: Colors.white,
                  ),
                  decoration: InputDecoration(
                      border: UnderlineInputBorder(borderSide: BorderSide.none),
                      prefixIcon: Icon(Icons.search, color: Colors.white),
                      hintText: "Search...",
                      hintStyle: TextStyle(color: Colors.white)),
                )
              : Text('My App Title'),
          centerTitle: true,
          actions: <Widget>[
            inSearch
                ? Container()
                : Center(
                    child: IconButton(
                      icon: Icon(Icons.search),
                      onPressed: () {
                        setState(() {
                          inSearch = true;
                        });
                      },
                    ),
                  ),
          ]),
      body: Container(),
    );
  }
}
0 голосов
/ 13 февраля 2020

Проблема в том, что от родительского виджета отсутствует обратный вызов, который позволяет узнать, когда текстовое поле было визуализировано, если оно динамически добавляется в дерево таким образом. Вместо этого просто перенесите запрос на фокус в новый виджет-оболочку, который знает, когда он рендерится в первый раз, и поэтому может запросить фокус после того, как рендеринг произошел, например:

import 'package:flutter/material.dart';

class TextWrapper extends StatefulWidget {
  const TextWrapper({
    this.controller,
    this.focusNode,
    this.onChanged,
  });

  final TextEditingController controller;
  final FocusNode focusNode;
  final ValueChanged<String> onChanged;

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

class _TextWrapperState extends State<TextWrapper> {
  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: widget.controller,
      focusNode: widget.focusNode,
      onChanged: widget.onChanged,
      style: TextStyle(
        color: Colors.white,
      ),
      decoration: InputDecoration(
        border: const UnderlineInputBorder(
          borderSide: BorderSide.none,
        ),
        prefixIcon: Icon(Icons.search, color: Colors.white),
        hintText: 'Search...',
        hintStyle: TextStyle(color: Colors.white),
      ),
    );
  }

  void _onRendered(Duration _) {
    FocusScope.of(context).requestFocus(widget.focusNode);
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addPostFrameCallback(_onRendered);
  }
}

Затем внутри вашего Scaffold() сделайте это вместо:

appBar: AppBar(
  title: appBarTitle,
  centerTitle: true,
  actions: <Widget>[
    Center(
      child: IconButton(
        icon: actionIcon,
        onPressed: () {
          setState(() {
            if (this.actionIcon.icon == Icons.search) {
              this.actionIcon = Icon(Icons.navigate_next);
              this.appBarTitle = TextWrapper(
                controller: editingController,
                focusNode: _searchInsideFocus,
                onChanged: (findNext) {
                  webView.findAllAsync(find: findNext);
                },
              );
            } else {
              webView.findNext(forward: true);
            }
          });
        },
      ),
    ),
  ],
),

Надеюсь, это поможет ...

...