4
\$\begingroup\$

The code works properly in a simplified version of the program I wrote. Here's the code:

import 'package:flutter/material.dart';

class GrandParent extends StatelessWidget {
  const GrandParent({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FocusNode Test'),
      ),
      body: Center(
        child: ElevatedButton(
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => const Parent()),
              );
            },
            child: Text('Go to Parent')),
      ),
    );
  }
}

class Parent extends StatefulWidget {
  const Parent({super.key});

  @override
  State<Parent> createState() => _ParentState();
}

class _ParentState extends State<Parent> {
  FocusNode? _focusNode1;
  FocusNode? _focusNode2;

  @override
  void initState() {
    super.initState();
    _focusNode1 = FocusNode();
    _focusNode2 = FocusNode();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _focusNode1!.requestFocus();
    });
  }

  @override
  void dispose() {
    _focusNode1!.dispose();
    _focusNode2!.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final bool isSmallScreen = MediaQuery.of(context).size.width < 600;

    return Scaffold(
      appBar: AppBar(
        title: Text('FocusNode Test'),
      ),
      body: Center(
        child: isSmallScreen
            ? ChildOne(
                focusNode1: _focusNode1,
                focusNode2: _focusNode2,
              )
            : ChildTwo(
                focusNode1: _focusNode1,
                focusNode2: _focusNode2,
              ),
      ),
    );
  }
}

class ChildOne extends StatelessWidget {
  final FocusNode? focusNode1;
  final FocusNode? focusNode2;
  const ChildOne(
      {super.key, required this.focusNode1, required this.focusNode2});

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.amber[600],
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Text(
            'Small Screen Widget',
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
          SizedBox(
            height: 8,
          ),
          TextField(
            focusNode: focusNode1,
            decoration: InputDecoration(hintText: 'FocusNode 1'),
          ),
          TextField(
            focusNode: focusNode2,
            decoration: InputDecoration(hintText: 'FocusNode 2'),
          ),
        ],
      ),
    );
  }
}

class ChildTwo extends StatelessWidget {
  final FocusNode? focusNode1;
  final FocusNode? focusNode2;
  const ChildTwo(
      {super.key, required this.focusNode1, required this.focusNode2});

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.amberAccent,
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Text('Large Screen Widget'),
          SizedBox(
            height: 8,
          ),
          TextField(
            focusNode: focusNode1,
            decoration: InputDecoration(hintText: 'FocusNode 1'),
          ),
          TextField(
            focusNode: focusNode2,
            decoration: InputDecoration(hintText: 'FocusNode 2'),
          ),
        ],
      ),
    );
  }
}

I've already put a breakpoint on the dispose() method's instance and tested the behavior of the FocusNode, which behaves as expected through the following output demonstration:

gif

I will also be monitoring the focus state of the FocusNode instance here.

My concern is, do I make it right? The primary reason why I am asking is that I was thinking if I need to use InheritedWidget class or if this given demonstration is considered production-ready.

Additionally, does this approach offer performance efficiency or not?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Your code is perfectly fine \$\endgroup\$ Commented Feb 20 at 19:57

1 Answer 1

3
\$\begingroup\$

I would personally scope the FocusNodes inside the children widgets because at every point in time, you would have one of these widgets rendered.

Your implementation will not fail because you are disposing of the FocusNodes in the parent widget, meaning during the resizing of the window, even if the other child widget (screen 1 or 2) is still in the widget tree [undisposed for some reason], flutter allows you to pass the same focus node to multiple widgets, so you will face no errors. But for more safety, try to scope each widget's state to its own because once it disposes, it will dispose of everything with it, like controllers, listeners, focusNodes, etc., unless you need to access that state outside of the child widget's lifecycle.

I know that probably you did that because you wanted to reduce the usage of stateful widgets, so in this case, I will urge you to learn about Hookwidgets because they exist to fix this issue and help you avoid converting everything to stateful widgets when working with controllers, animations, counters, etc.

\$\endgroup\$
4
  • \$\begingroup\$ Interesting; it sounds clear to me, but I was thinking of which is the best thing to do. Nevertheless, you made a good suggestion about Hookwidgets, and you explained it very well (I guess). \$\endgroup\$ Commented Mar 22 at 1:21
  • 1
    \$\begingroup\$ thank you! As I said, the best approach is to treat widgets as components. Remember, we don't dispose of widgets manually, the flutter engine does (not technically but we can say that). Meaning it's only valid to make sure when a widget disposes it takes everything related to it with it. This example is quite small, but for large widgets, I highly advise against it because you might have these controllers living in memory while you don't even need them anymore. \$\endgroup\$ Commented Mar 22 at 1:40
  • 1
    \$\begingroup\$ Now I see the other impact of it in other case scenarios, and I agree with that, but maybe if the proper disposal isn't handled correctly. Then, it might cause either a bottleneck or an overhead. \$\endgroup\$ Commented Mar 22 at 1:48
  • \$\begingroup\$ Exactly! Thank you for your opinion \$\endgroup\$ Commented Mar 22 at 1:58

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.