skip to Main Content

I am developing a tool that generates dart code and I am facing this runtime error when executing a program having a circular dependency.

file.dart:xx:xx: Error: Constant evaluation error:
    this.b = const B(),,
                   ^
file:xx:xx: Context: Constant expression depends on itself.
    this. A = const A(),

Here is a simplification of the program I am executing:

class A {
  final B b;
  const A({
    this.b = const B(),
  });
}

class B {
  final A a;
  const B({
    this.a = const A(),
  });
}

As you can see there is a circular dependency between A and B.

I tried dropping the const keyword as:

class A {
  final B b;
  A({
    this.b = B(),
  });
}

class B {
  final A a;
  B({
    this.a = A(),
  });
}

But instead, I am getting a compile time error:

The default value of an optional parameter must be constant. dart(non_constant_default_value)

Do you have an idea how to handle this type of issue? Or is there an equivalent of forwardref in Dart?

Edit

At the current state of dart, it is not possible to avoid this circular dependency. I ended restricting this case. If the input has a circular dependency, it is normal that the output will have it.

2

Answers


  1. How about using factory constructors?

    class A {
      final B b;
      
      factory A() => A._(B());
      
      A._(this.b);
    }
    
    class B {
      final A a;
      
      factory B() => B._(A());
      
      B._(this.a);
    }
    
    Login or Signup to reply.
  2. Your approaches don’t work because when you construct A(), it will construct a B() which will construct A()which will construct B(), ad infinitum. You need to break the infinite recursion loop.

    If you want a single instance of A and a single instance of B to have references to each other, you will need to have one of them pass a reference to itself instead of letting the other constructor create a new instance of the initial type. You can’t pass this in an initializer list (this is not yet valid at that point), so it must be done in the constructor body, and the initialized final members will need to be late. The constructor body also means that both classes cannot have const constructors.

    If you want const constructors because you want to have A() and B() as default arguments, the typical approach is to use null as a default.

    class A {
      late final B b;
      A({B? b}) {
        this.b = b ?? B(a: this);
      }
    }
    
    class B {
      late final A a;
      B({A? a}) {
        this.a = a ?? A(b: this);
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search