They were originally intended to be developer-facing objects used to control
some aspects of the focus system, but over time they have evolved to mostly
implement details of the focus system. In order to prevent breaking existing
applications, they still contain public interfaces for their attributes. But, in
general, the thing for which they are most useful is to act as a relatively
opaque handle, passed to a descendant widget in order to call requestFocus()
on an ancestor widget, which requests that a descendant widget obtain focus.
Setting of the other attributes is best managed by a Focus or
FocusScope widget, unless you are not using them, or implementing your own
version of them.

Best practices for creating FocusNode objects

Some dos and don’ts around using these objects include:

Don’t allocate a new FocusNode for each build.  This can cause
memory leaks, and occasionally causes a loss of focus when the widget
rebuilds while the node has focus.