Flutter Focus 关注多个节点而不是单个节点

Flutter Focus is focussing multiple nodes instead of a single node

我正在构建一个将由方向键键盘事件控制的应用程序。所以我研究了 flutter Focus 系统。我想给它特别的指示,因为在我的应用程序中我想使用多个“控制区”,在我的示例中我添加了 2 个区域,A 和 B。

此时,当我按下左箭头时,它会聚焦在左侧 (A) 区域。当我按下右箭头时,它会聚焦右 (B) 区域。但是是集中在“区域”中的所有按钮吗?
实际行为:

预期的行为是切换区域并使用 left/right 键聚焦一个按钮。使用 up/down 键,它应该在活动区域​​内的焦点内切换按钮。

这是我的代码:

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

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

class _FocusTestState extends State<FocusTest> {
  static bool _aIsSelected = true;
  final FocusScopeNode _focusScopeNodeA = FocusScopeNode();

  static bool _bIsSelected = false;
  final FocusScopeNode _focusScopeNodeB = FocusScopeNode();

  @override
  void initState() {
    super.initState();

    RawKeyboard.instance.addListener(_handleDpad);
  }

  @override
  void dispose() {
    _focusScopeNodeA.dispose();
    _focusScopeNodeB.dispose();
    RawKeyboard.instance.removeListener(_handleDpad);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Row(
          children: [
            Expanded(
              flex: 45,
              child: Container(
                height: 155,
                color: Colors.grey[350],
                child: Focus(

                  child: Column(
                    children: [
                      TextButton(
                        onPressed: () { print("a1");},
                        focusNode: _focusScopeNodeA,
                        autofocus: true,
                        child: const Text("a 1"),
                      ),
                      TextButton(
                        onPressed: () { print("a2");},
                        focusNode: _focusScopeNodeA,
                        autofocus: false,
                        child: const Text("a 2"),
                      ),
                      TextButton(
                        onPressed: () { print("a3");},
                        focusNode: _focusScopeNodeA,
                        autofocus: false,
                        child: const Text("a 3"),
                      ),
                    ],
                  ),
                ),
              ),
            ),
            Expanded(
                flex: 10,
                child: Container()),
            Expanded(
              flex: 45,
                child: Container(
                  height: 155,
                  color: Colors.grey[350],
                  child: Column(
                    children: [
                      TextButton(
                        onPressed: () { print("b1");},
                        focusNode: _focusScopeNodeB,
                        autofocus: false,
                        child: const Text("b 1"),
                      ),
                      TextButton(
                        onPressed: () { print("b2");},
                        focusNode: _focusScopeNodeB,
                        autofocus: false,
                        child: const Text("b 2"),
                      ),
                      TextButton(
                        onPressed: () { print("b3"); },
                        focusNode: _focusScopeNodeB,
                        autofocus: false,
                        child: const Text("b 3"),
                      ),
                    ],
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }

  KeyEventResult _handleDpad(RawKeyEvent event) {
    //debugDumpFocusTree();
    if (event.runtimeType == RawKeyUpEvent) {
      if (event.logicalKey == LogicalKeyboardKey.arrowLeft) {
        if (kDebugMode) {
          print("left button");
        }
        _aIsSelected = true;
        _bIsSelected = false;

        _focusScopeNodeA.requestFocus();
        return KeyEventResult.handled;
      }
      if (event.logicalKey == LogicalKeyboardKey.arrowRight) {
        if (kDebugMode) {
          print("right button");
        }
        _aIsSelected = false;
        _bIsSelected = true;

        _focusScopeNodeB.requestFocus();
        return KeyEventResult.handled;
      }
      if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
        if (kDebugMode) {
          print("down button");
        }
        if (_aIsSelected) {
          _focusScopeNodeA.nextFocus();
        }
        if (_bIsSelected) {
          _focusScopeNodeB.nextFocus();
        }
        return KeyEventResult.handled;
      }
      if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
        if (kDebugMode) {
          print("up button");
        }

        if (_aIsSelected) {
          _focusScopeNodeA.previousFocus();
        }
        if (_bIsSelected) {
          _focusScopeNodeB.previousFocus();
        }
        return KeyEventResult.handled;
      }
    }
    return KeyEventResult.ignored;
  }

或查看:
https://gist.github.com/dixi83/2b2d0a63fe465baa09210be9d34fd194

我也尝试了 unfocus() 方法,但因为它没有任何区别,所以我在示例中将其保留。

更新 1:

discord 上的某个人参加了我对 3 个按钮使用相同的 FocusNode 的讨论。他是对的,但奇怪的是在我发布这个问题之前,我有一个包含 2 个焦点节点列表的版本,我尝试用指针处理焦点。这个版本向我展示了完全相同的行为...这是源代码:

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

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

class _FocusDemoState extends State<FocusDemo> {
  static const int _nrOfNodesA = 3;
  static int _focusPointerA = 0;
  static bool _aIsSelected = true;
  final List<FocusNode> _focusNodesA = List.filled(_nrOfNodesA, FocusNode());

  static const int _nrOfNodesB = 3;
  static int _focusPointerB = 0;
  static bool _bIsSelected = false;
  final List<FocusNode> _focusNodesB = List.filled(_nrOfNodesB, FocusNode());

  @override
  void initState() {
    super.initState();

    _focusNodesA[0].hasPrimaryFocus;
    _focusNodesA[0].requestFocus();

    RawKeyboard.instance.addListener(_handleDpad);
  }

  @override
  void dispose() {
    for(int i=0; i < _nrOfNodesA; i++){
      _focusNodesA[i].dispose();
    }
    for(int i=0; i < _nrOfNodesB; i++){
      _focusNodesB[i].dispose();
    }
    RawKeyboard.instance.removeListener(_handleDpad);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Row(
          children: [
            Expanded(
              flex: 45,
              child: Container(
                height: 155,
                color: Colors.grey[350],
                child: Focus(

                  child: Column(
                    children: [
                      TextButton(
                        onPressed: () { print("a1");},
                        focusNode: _focusNodesA[0],
                        autofocus: true,
                        child: const Text("a 1"),
                      ),
                      TextButton(
                        onPressed: () { print("a2");},
                        focusNode: _focusNodesA[1],
                        autofocus: false,
                        child: const Text("a 2"),
                      ),
                      TextButton(
                        onPressed: () { print("a3");},
                        focusNode: _focusNodesA[2],
                        autofocus: false,
                        child: const Text("a 3"),
                      ),
                    ],
                  ),
                ),
              ),
            ),
            Expanded(
                flex: 10,
                child: Container()),
            Expanded(
              flex: 45,
                child: Container(
                  height: 155,
                  color: Colors.grey[350],
                  child: Column(
                    children: [
                      TextButton(
                        onPressed: () { print("b1");},
                        focusNode: _focusNodesB[0],
                        autofocus: false,
                        child: const Text("b 1"),
                      ),
                      TextButton(
                        onPressed: () { print("b2");},
                        focusNode: _focusNodesB[1],
                        autofocus: false,
                        child: const Text("b 2"),
                      ),
                      TextButton(
                        onPressed: () { print("b3"); },
                        focusNode: _focusNodesB[2],
                        autofocus: false,
                        child: const Text("b 3"),
                      ),
                    ],
                  ),
                ),
              ),
          ],
        ),
      ),
    );
  }

  KeyEventResult _handleDpad(RawKeyEvent event) {
    //debugDumpFocusTree();
    if (event.runtimeType == RawKeyUpEvent) {
      if (event.logicalKey == LogicalKeyboardKey.arrowLeft) {
        if (kDebugMode) {
          print("left button");
        }
        _aIsSelected = true;
        _bIsSelected = false;

        _focusPointerA = 0;
        _focusPointerB = 0;

        _focusNodesA[0].requestFocus();

        return KeyEventResult.handled;
      }
      if (event.logicalKey == LogicalKeyboardKey.arrowRight) {
        if (kDebugMode) {
          print("right button");
        }
        _aIsSelected = false;
        _bIsSelected = true;
        _focusPointerA = 0;
        _focusPointerB = 0;

        _focusNodesB[0].requestFocus();

        return KeyEventResult.handled;
      }
      if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
        if (kDebugMode) {
          print("down button");
        }
        _focusPointerA--;
        _focusPointerB--;

        if (_focusPointerA < 0) {
          _focusPointerA = 0;
        }
        if (_focusPointerB < 0) {
          _focusPointerB = 0;
        }
        if (_aIsSelected) {
          _focusNodesA[_focusPointerA].requestFocus();
        }
        if (_bIsSelected) {
          _focusNodesB[_focusPointerB].requestFocus();
        }
        return KeyEventResult.handled;
      }
      if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
        if (kDebugMode) {
          print("up button");
        }
        _focusPointerA++;
        _focusPointerB++;

        if (_focusPointerA >= _nrOfNodesA) {
          _focusPointerA = _nrOfNodesA-1;
        }
        if (_focusPointerB >= _nrOfNodesB) {
          _focusPointerB = _nrOfNodesA-1;
        }
        if (_aIsSelected) {
          _focusNodesA[_focusPointerA].requestFocus();
        }
        if (_bIsSelected) {
          _focusNodesB[_focusPointerB].requestFocus();
        }
        return KeyEventResult.handled;
      }
    }
    return KeyEventResult.ignored;
  }
}

或查看:
https://gist.github.com/dixi83/f653022ad1bdb1d41dcfcf0f1d278b8b

我的第一次尝试(见更新 1)似乎比我想象的更接近解决方案......我的错误是我使用 List.filled() 而不是 List.generate() 区别? .

完美地解释了这一点
final List<FocusNode> _focusNodeList = List.generate(_nrOfNodes, (_) => FocusNode());