Q: What is the difference between using ConstrainedBox and Container widgets to give constraints to their child box? I found that the only difference between the Container() and ConstrainedBox() widgets (if we want to constrain its child), is the more properties in the Container() widget to customize its child, but are there any other differences? and are there any performance differences and when it is efficient to use what?

A: Container does not do anything by itself. It is only a utility widget that delegates functionality to other widgets. This means that the contraints argument of a Container is strictly equivalent to ConstrainedBox.
If you take a look at the source code for Container, you will find the following:
    if (constraints != null)
      current = ConstrainedBox(constraints: constraints, child: current);

→ there is no difference.
Container(
  constraints: constraints,
  child: child,
)
// does strictly the same as
ConstrainedBox(
  constraints: constraints,
  child: child,
)

