Flutter как переключать фокус между текстовыми полями в сетке (кроссворд)

#flutter #focus #textfield

#flutter #фокус #текстовое поле

Вопрос:

Я пытаюсь создать кроссворд. Я создаю сетку кроссвордов с помощью GridView.count. Я получаю шаблон кроссворда из списка, черные квадраты отмечены # и являются просто черными квадратами, с которыми вы ничего не можете сделать, как и должно быть. Остальные ячейки сетки — это текстовые поля, в которые пользователь может вписать буквы, чтобы составить кроссворд. Каждый кроссворд отличается, поэтому мне приходится создавать сетку программно. Моя проблема в том, что мне нужно выяснить, является ли слово, которое пользователь хочет написать, горизонтальным или вертикальным, и переключить фокус с первой на следующую ячейку (белый квадрат), пока пользователь пишет. Как указать, в какую сторону двигаться (горизонтально или вертикально)? Я также узнал о FocusTraversalGroup и FocusTraversalOrder, но никаких примеров о том, как их использовать.

Вот код (в коде s находится список, содержащий шаблон кроссворда):

отредактированный код после предложения @DungNgo:

 class Cells extends StatefulWidget {
  @override
  _CellsState createState() => _CellsState();
}

class _CellsState extends State<Cells> {

  final FocusScopeNode _node = FocusScopeNode(); //new code
  
  @override
  void dispose() {
    _node.dispose(); //new code
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Crossword'),
        ),
        body: SafeArea(
            child: GridView.count(
              crossAxisCount: 15,
              mainAxisSpacing: 2,
              crossAxisSpacing: 2,
              children: List.generate(s.length, (index) {
                return FocusScope( //new code
                    node: _node, //new code
                  child: Container(
                  width: 40,
                  height: 40,
                  decoration: BoxDecoration(
                      color: s[index] == '#' ? Colors.black : Colors.white,
                      border: Border.all(color: Colors.black)
                  ),
                  child:
                  s[index] == '#' ?
                  Text( '#' ) :
                 TextField(
                      cursorColor: Colors.black,
                      textAlign: TextAlign.center,
                      textAlignVertical: TextAlignVertical.bottom,
                      decoration: InputDecoration.collapsed(hintText: ""),
                      style: TextStyle( decoration: TextDecoration.none),
                      onChanged: (text){
                      _node.nextFocus(); //new code
                    },
                  ),
                 )
                );
              }),
            )
        )
    );

  }
} 
  

Теперь курсор автоматически фокусируется на следующей ячейке, но только по горизонтали. Кто-нибудь знает, как заставить его «перемещаться» по вертикали? Я знаю, что DirectionalFocusTraversalPolicyMixin существует, и я вижу это

 focusInDirection(TraversalDirection direction) => FocusTraversalGroup.of(context!)!.inDirection(this, direction);
  

но я понятия не имею, как это реализовать!
Любая помощь будет принята с благодарностью.
Спасибо

Комментарии:

1. Для 2-го вопроса вы можете использовать FocusScope.of(context).nextFocus() и поместить его в OnChanged() текстового поля.

2. @Dung Ngo спасибо, я пытался, но у меня получилось странное зацикливание от текстового поля к текстовому полю… Я попробую еще раз удалить узлы фокусировки в другом месте, возможно, в этом и была проблема … есть идеи, как переместить фокус по вертикали? Я думаю, по умолчанию используется горизонтально…

3. Кто-нибудь? Даже просто указатель в каком-то направлении? пробовал все, что мог придумать, и никакой радости…

Ответ №1:

После долгих размышлений я наконец нашел способ сделать это. Это код:

 class Cells extends StatefulWidget {
  @override
  _CellsState createState() => _CellsState();
}

class _CellsState extends State<Cells> {

  isVertical = false; // added a flag
  final FocusScopeNode _node = FocusScopeNode(); 
  
  @override
  void dispose() {
    _node.dispose();
    super.dispose();
  }
  
  // added a diolog to let the user choose the writing direction (horizontal or vertical)

   void _showDialog() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text("Direzione"),
          content: Text("Scrivi in verticale?"),
          actions: <Widget>[
            FlatButton(
              child: Text("SI"),
              onPressed: () {
                setState(() {
                  isVertical = true;
                });
                Navigator.of(context).pop();
              },
            ),
            FlatButton(
              child: Text("NO"),
              onPressed: () {
                setState(() {
                  isVertical = false;
                });
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Crossword'),
        ),
        body: SafeArea(
            child: GridView.count(
              crossAxisCount: 15,
              mainAxisSpacing: 2,
              crossAxisSpacing: 2,
              children: List.generate(s.length, (index) {
                return FocusScope( 
                    node: _node,
                  child: Container(
                  width: 40,
                  height: 40,
                  decoration: BoxDecoration(
                      color: s[index] == '#' ? Colors.black : Colors.white,
                      border: Border.all(color: Colors.black)
                  ),
                  child:
                  s[index] == '#' ?
                  Text( '#' ) :
                 TextField(
                      cursorColor: Colors.black,
                      textAlign: TextAlign.center,
                      textAlignVertical: TextAlignVertical.bottom,
                      decoration: InputDecoration.collapsed(hintText: ""),
                      style: TextStyle( decoration: TextDecoration.none),
                      onChanged: (text){
                      isVertical == false ? _node.focusInDirection(TraversalDirection.right) : _node.focusInDirection(TraversalDirection.down); // this is what does it !!
                    },
                  ),
                 )
                );
              }),
            )
        )
    );

  }
} 
  

Есть еще много вещей, которые нужно исправить, но на мой главный вопрос дан ответ.