Flutter FocusNode сфокусируйтесь на этой вещи-(1)

Flutter

Много раз флаттер должен иметь дело с фокусом ввода, давайте посмотрим, как использовать элемент управления сегодня.

Эту статью можно рассматривать как простое использование, и она не будет углубляться в исходный код, чтобы обсуждать, как прикрепить, в основном использование элементов управления серии Focus, и как многократно переходить между несколькими полями ввода.

Описание окружающей среды

  1. Эта статья в основном основана на версии 1.17.5 flutter sdk, другие версии должны быть похожими, но со временем многое может измениться, пожалуйста, продолжайте проверять, действительна ли она в будущем.
  2. Эта статья предназначена в основном для мобильных устройств.
  3. На момент написания этой статьи фокус Flutter Web был довольно запутанным, и он, кажется, отличается от мобильной версии, поэтому я пока пропущу его.
  4. Настольная версия была опробована только на macOS, других настольных движках, проверьте сами.

Связанные классы дартс

Во флаттере классы, связанные с фокусом, следующие:

  • FocusNode: можно сказать, что это наиболее часто используемый, один из основных классов
  • FocusManager: Класс Singleton, эта штука обрабатывает ядро ​​управления фокусом всего приложения флаттера, включая такие операции, как всплывающее окно программной клавиатуры с собственным взаимодействием.
  • Focus: виджет, используемый для «добавления» возможностей фокусировки к элементу управления, просто завершите его,InkWellСпособность таких элементов управления получать фокус зависит от этой вещи.
  • FocusScope: Виджет, подкласс Фокуса, все подвиджеты, обернутые этой вещью.FocusNodeбудут автоматически зарегистрированы в нем и примут единое управление
  • FocusScopeNode: Эта штука сама по себе является подклассом FocusNode, но в основном используется дляFocusScopeб/у, расширенныйFocusNodeповедение
  • FocusTraversalPolicy, FocusTraversalGroup: Эти две вещи являются стратегией узла фокуса, который используется для сортировки следующего фокуса. Эти две вещи не должны быть рассмотрены в этой статье. Если вам интересно, вы можете перейти к официальной документации. В настоящее время я думаю его не следует использовать.

FocusNode

Об этом много говорят, поэтому я не буду распространяться об этом, просто кратко расскажу о нескольких методах.

  • canRequestFocus: запрашивать ли фокус
  • context: BuildContext виджета, фокус которого "прикреплен"
  • hasFocus: есть ли фокус
  • unfocus: отказаться от фокуса, если текущий узел имеет фокус и это вызывается, фокус будет передан, если в то же время появится программная клавиатура, программная клавиатура будет закрыта.
  • requestFocus: запросить фокус, после вызова этого метода он переместит фокус на текущий

Примечания: Есть много других методов, которые трудно использовать для обычных друзей и нормальных сценариев приложений. Они предоставляются в качестве программной структуры, но личные мнения не должны быть поняты, если вы знаете основные методы

FocusManager

Этот вариант осуществления является одной вещьюFocusManager.instanceПолучать

Есть общий способ понять:FocusManager.instance.primaryFocus.unfocus();, назови это, софт клава опустится

1595307999

В основном в этой штуке есть приватные методы, и не так много их можно вызвать.

FocusHighlightMode Эта штука "режим" фокуса, соответствующий прикосновению и мышке и клавиатуре. Лично я не думаю, что он используется в нормальных условиях, достаточно прикосновения на мобильном терминале.

Focus

Эта штука вообще редко используется, в SDK есть кое-какие места, которые будут использоваться,FocusСам внутренний объект поддерживаетFocusNode, например, кнопка может реагировать на фокус клавиатуры и вводить потому что внутри что-то есть

Этот класс не очень часто используется в проекте флаттера, но он является ключевым.

1595308605
1595311067

_FocusableActionDetectorState: соответствуетFocusableActionDetectorсостояние, этот класс используется дляCheckBox, Radio, Switch

FocusScope

Эта вещь редко встречается в документах. Здесь я кратко проанализирую ее. Можно также сказать, что это будет предметом дальнейшего использования. Когда я сталкиваюсь с полем ввода в реальной разработке, этот элемент управления является моим первым выбором.

Проще говоря, именно в этом субуправленииFocusNodeбудет поддерживаться равномерно

1595316377

Метод построения этой штуки может передавать некоторые параметры, и наиболее часто используемые из них — node, canRequestFocus и тому подобное.

Вот skipTraversal, этот параметр можно объяснить позже на примере

FocusScopeNode

вообще иFocusScopeиспользовать в паре

написать код

написание начального уровня

Ну а спереди все концептуально, многие мои друзья не хотят его видеть, да и не интересно

Например, есть такая сцена

1595317002

В случае с приложением есть 4 поля ввода, и вы можете нажимать одно за другим, но если вы хотите получить хороший пользовательский опыт, разве вы не должны нажимать Enter до следующего шага, а затем отправить последний напрямую?

Смоделируйте, сколько людей пишут эту вещь

1595317293

Хм, пересмотрите это, хм, это аккуратно, так что... что вы делаете, когда у вас есть 10?

Может, перепишем?

1595317499

Хорошо, при условии, что у вас есть прочная основа, естественно писать так.

Передовой

Вышеупомянутое письмо очень быстрое, но не трепетное, мы можем изменить написание трепетания на это

import 'package:flutter/material.dart';

class Example3 extends StatefulWidget {
  @override
  _Example3State createState() => _Example3State();
}

class _Example3State extends State<Example3> {
  FocusScopeNode node = FocusScopeNode();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: FocusScope(
        node: node,
        child: SingleChildScrollView(
          child: Column(
            children: <Widget>[
              for (var i = 0; i < 10; i++) buildTextField(),
            ],
          ),
        ),
      ),
    );
  }

  TextField buildTextField() {
    return TextField(
      onEditingComplete: () {
        if (node.focusedChild == node.children.last) {
          print('submit');
        } else {
          node.nextFocus();
        }
      },
    );
  }
}

На этот раз дажеFocusNodeВам не нужно писать его самостоятельно, просто используйте его непосредственно в Scope.

Как выглядит этот пример:

1595319363

Это потому чтоTextFieldдаEditableTextупаковка

1595318868
1595318898
1595318882

Затем в EditableText прикрепите его к контексту

1595319082

Увидев это, вы обнаружили, что некоторые вещи на самом деле очень просты, а потом их усложняют?

Передовой

import 'package:flutter/material.dart';

class Example3 extends StatefulWidget {
  @override
  _Example3State createState() => _Example3State();
}

class _Example3State extends State<Example3> {
  FocusScopeNode node = FocusScopeNode();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: FocusScope(
        node: node,
        child: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              children: <Widget>[
                for (var i = 0; i < 5; i++) buildTextField(),
                Row(
                  children: <Widget>[
                    Expanded(
                      child: TextField(
                        onEditingComplete: onEdit,
                      ),
                    ),
                    RaisedButton(
                      onPressed: () {},
                      child: Text('假装获取验证码'),
                    ),
                  ],
                ),
                for (var i = 0; i < 5; i++) buildTextField(),
              ],
            ),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          print(node.traversalChildren.length);
        },
        child: Icon(Icons.check),
      ),
    );
  }

  TextField buildTextField() {
    return TextField(
      onEditingComplete: onEdit,
    );
  }

  void onEdit() {
    node.nextFocus();
  }
}

Иногда рядом с этим видом есть кнопка, что является относительно распространенным способом, и тогда приведенный выше код внезапно не работает должным образом.

1595323601

Тогда вам нужно изменить код

floatingActionButton: FloatingActionButton(
onPressed: () {
    print(node.children.length); // 12
},
child: Icon(Icons.check),
),

Почему оно изменено на 12, разве там не только 11 полей ввода?

Именно об этом я и говорил в начале, у многих кнопок тоже есть фокус.

Итак, как пропустить эту кнопку при входе

   RaisedButton(
    onPressed: () {},
    focusNode: FocusNode(skipTraversal: true),
    child: Text('假装获取验证码'),
),

Да, все, вручную передать кнопку дляFocusNode, затем пропустить работы

1595324883

Полный код:

import 'package:flutter/material.dart';

class Example3 extends StatefulWidget {
  @override
  _Example3State createState() => _Example3State();
}

class _Example3State extends State<Example3> {
  FocusScopeNode node = FocusScopeNode();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: FocusScope(
        node: node,
        child: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              children: <Widget>[
                for (var i = 0; i < 5; i++) buildTextField(),
                Row(
                  children: <Widget>[
                    Expanded(
                      child: TextField(
                        onEditingComplete: onEdit,
                      ),
                    ),
                    RaisedButton(
                      onPressed: () {},
                      focusNode: FocusNode(skipTraversal: true),
                      child: Text('假装获取验证码'),
                    ),
                  ],
                ),
                for (var i = 0; i < 5; i++) buildTextField(),
              ],
            ),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          print(node.traversalChildren.length);
        },
        child: Icon(Icons.check),
      ),
    );
  }

  TextField buildTextField() {
    return TextField(
      onEditingComplete: onEdit,
    );
  }

  void onEdit() {
    node.nextFocus();
  }
}

Итак, резюмируя шаги

  1. обернуть все поля ввода в одноFocusScopeвнутри, настройкиFocusScopeNode.
  2. Установите элемент управления, который имеет фокус, но не является полем вводаFocusNode(skipTraversal: true)
  3. использоватьFocusScopeNodeизnextFocusметод

постскриптум

На данный момент в этой статье ожидается, что продолжение этой серии будет глубоко проникать в исходный код.

над