'Flutter Focus is focussing multiple nodes instead of a single node

I am building an app that is going to be controlled by a D-pad keyboard events. So I dug into the flutter Focus system. I want to give it special directions because in my app I want to work with multiple "control zones" in my example I added 2 zones, A and B.

At this moment, when I push the left arrow it focuses in the left (A) area. When I push the right arrow it focuses the right (B) area. But is focuses all the buttons in the "zone"??
Actual behavior: actual behavior of my code

The expected behavior is that it switches the zone and focuses one button with the left/right keys. With the up/down keys it should switch buttons inside the focus inside the active zone.

here is my code:

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;
  }

or see:
https://gist.github.com/dixi83/2b2d0a63fe465baa09210be9d34fd194

I tried also the unfocus() method but because is did not make any difference I left it away in my example.

Update 1:

Someone on discord attended me on using the same FocusNode for 3 buttons. He is right but the strange thing is before I posted this question, I had a version with 2 lists of focus nodes where I tried handling the focus with a pointer. This version showed me the exact the same behavior... here is the source code:

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;
  }
}

or see:
https://gist.github.com/dixi83/f653022ad1bdb1d41dcfcf0f1d278b8b



Solution 1:[1]

With my first attempt (See Update 1) it seems that I was more close to the solution than I thought... My mistake is that I used List.filled() in stead of List.generate() The difference? This is perfectly explained by danypata.

final List<FocusNode> _focusNodeList = List.generate(_nrOfNodes, (_) => FocusNode());

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1