It's one of those rules that makes total sense the first time you here it.

It's actually outlined concisely in the style guide: DO annotate with Object instead of dynamic to indicate any object is accepted.

// **Use Object**
// Accepts any object.
void log(Object object) {
  print(object.toString());
}

// **Use dynamic**
// Only accepts bool or String, which can't be expressed in a type annotation.
// Probably should document what we expect arg to be.
bool convertToBool(dynamic arg) {
  if (arg is bool) return arg;
  if (arg is String) return arg == 'true';
  throw new ArgumentError('Cannot convert $arg to a bool.');
}

The way I like to think about it: dynamic is our crutch until Dart has union types.

Depending on whom you ask, explicitly specifying dynamic in convertToBool is unnecessary. But I like being explicit.

Happy hacking!