Generate unique form ID based on context
As with Drupal 7, forms need to have a unique form ID. What's different is that in Drupal 8, the connection between the form (code) and the form ID is reversed.

In Drupal 7, as pointed out by @Ivan Jaros in the comment, you start with the form_id, so we had hook_forms() that allowed to map form ID's to a function that then built the form.

In Drupal 8, you first need to create a form object so that its getFormId() method can return a unique ID. The challenge there is that getFormId() is called very early, specifically before buildForm(), so the usual way of passing arguments to forms does not work.

What you need to do is create the form object yourself instead of just passing the class name to the form builder, then you can set any data on it that you want before passing it the form builder.

To get the form class, you can either instantiate it yourself, but then you can't use the ::create() method to inject dependencies, for that, you need to use the class resolver, from FormBuilder::getFormId(), this is what it does by default if you pass in a class name:

if (is_string($form_arg) &amp;&amp; class_exists($form_arg)) {
  $form_arg = $this-&gt;classResolver-&gt;getInstanceFromDefinition($form_arg);
}


You could even use the current object or any other existing object as a form, as long as it implements \Drupal\Core\Form\FormInterface. One example in core that does that is the DraggableListBuilder and BlockListBuilder, see its render() method:

public function render() {
  if (!empty($this-&gt;weightKey)) {
    return $this-&gt;formBuilder()-&gt;getForm($this);
  }
  return parent::render();
}


That has the advantage that you do not need another class, you could make a block plugin that displays a form do it in the same class, for example. On the other side, that prevents you from re-using that form elsewhere and could also result in conflicts if the same method names are used for something else.