pub outdated: added clear message when no outdated packages. (#2898)

diff --git a/lib/src/command/outdated.dart b/lib/src/command/outdated.dart
index 843b306..97d71cb 100644
--- a/lib/src/command/outdated.dart
+++ b/lib/src/command/outdated.dart
@@ -573,10 +573,15 @@
       }
     }
 
-    if (notAtResolvable == 0 && upgradable == 0 && rows.isNotEmpty) {
+    if (notAtResolvable == 0 &&
+        upgradable == 0 &&
+        rows.isNotEmpty &&
+        (directRows.isNotEmpty || devRows.isNotEmpty)) {
       log.message(
           "You are already using the newest resolvable versions listed in the 'Resolvable' column.\n"
           "Newer versions, listed in 'Latest', may not be mutually compatible.");
+    } else if (directRows.isEmpty && devRows.isEmpty) {
+      log.message(mode.allSafe);
     }
   } else {
     log.message('\nNo pubspec.lock found. There are no Current versions.\n'
@@ -608,6 +613,7 @@
   String get allGood;
   String get noResolutionText;
   String get upgradeConstrained;
+  String get allSafe;
 
   Future<Pubspec> resolvablePubspec(Pubspec pubspec);
 }
@@ -634,6 +640,9 @@
       'edit pubspec.yaml, or run `$topLevelProgram pub upgrade --major-versions`';
 
   @override
+  String get allSafe => 'all dependencies are up-to-date.';
+
+  @override
   Future<List<List<_MarkedVersionDetails>>> markVersionDetails(
       List<_PackageDetails> packages) async {
     final rows = <List<_MarkedVersionDetails>>[];
@@ -714,6 +723,9 @@
       'edit pubspec.yaml, or run `$topLevelProgram pub upgrade --null-safety`';
 
   @override
+  String get allSafe => 'All dependencies opt in to null-safety.';
+
+  @override
   Future<List<List<_MarkedVersionDetails>>> markVersionDetails(
       List<_PackageDetails> packages) async {
     final nullSafetyMap =
diff --git a/test/outdated/goldens/null_safety_already_migrated.txt b/test/outdated/goldens/null_safety_already_migrated.txt
new file mode 100644
index 0000000..b7ad48e
--- /dev/null
+++ b/test/outdated/goldens/null_safety_already_migrated.txt
@@ -0,0 +1,121 @@
+$ pub outdated --json
+{
+  "packages": []
+}
+
+$ pub outdated --no-color
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Found no outdated packages
+
+$ pub outdated --no-color --no-transitive
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Found no outdated packages
+
+$ pub outdated --no-color --up-to-date
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Package Name   Current  Upgradable  Resolvable  Latest  
+
+direct dependencies:
+foo            2.0.0    2.0.0       2.0.0       2.0.0   
+
+dev_dependencies:
+bar            2.0.0    2.0.0       2.0.0       2.0.0   
+
+transitive dev_dependencies:
+devTransitive  1.0.0    1.0.0       1.0.0       1.0.0   
+You are already using the newest resolvable versions listed in the 'Resolvable' column.
+Newer versions, listed in 'Latest', may not be mutually compatible.
+
+$ pub outdated --no-color --prereleases
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Found no outdated packages
+
+$ pub outdated --no-color --no-dev-dependencies
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Found no outdated packages
+
+$ pub outdated --no-color --no-dependency-overrides
+Showing outdated packages.
+[*] indicates versions that are not the latest available.
+
+Found no outdated packages
+
+$ pub outdated --no-color --mode=null-safety
+Showing dependencies that are currently not opted in to null-safety.
+[✗] indicates versions without null safety support.
+[✓] indicates versions opting in to null safety.
+
+Package Name  Current  Upgradable  Resolvable  Latest  
+
+direct dependencies: all support null safety.
+
+dev_dependencies: all support null safety.
+All dependencies opt in to null-safety.
+
+$ pub outdated --no-color --mode=null-safety --transitive
+Showing dependencies that are currently not opted in to null-safety.
+[✗] indicates versions without null safety support.
+[✓] indicates versions opting in to null safety.
+
+Package Name   Current  Upgradable  Resolvable  Latest  
+
+direct dependencies: all support null safety.
+
+dev_dependencies: all support null safety.
+
+transitive dev_dependencies:
+devTransitive  ✗1.0.0   ✗1.0.0      ✗1.0.0      ✗1.0.0  
+All dependencies opt in to null-safety.
+
+$ pub outdated --no-color --mode=null-safety --no-prereleases
+Showing dependencies that are currently not opted in to null-safety.
+[✗] indicates versions without null safety support.
+[✓] indicates versions opting in to null safety.
+
+Package Name  Current  Upgradable  Resolvable  Latest  
+
+direct dependencies: all support null safety.
+
+dev_dependencies: all support null safety.
+All dependencies opt in to null-safety.
+
+$ pub outdated --json --mode=null-safety
+{
+  "packages": [
+    {
+      "package": "devTransitive",
+      "current": {
+        "version": "1.0.0",
+        "nullSafety": false
+      },
+      "upgradable": {
+        "version": "1.0.0",
+        "nullSafety": false
+      },
+      "resolvable": {
+        "version": "1.0.0",
+        "nullSafety": false
+      },
+      "latest": {
+        "version": "1.0.0",
+        "nullSafety": false
+      }
+    }
+  ]
+}
+
+$ pub outdated --json --no-dev-dependencies
+{
+  "packages": []
+}
+
diff --git a/test/outdated/outdated_test.dart b/test/outdated/outdated_test.dart
index 1c0a28d..c815bb0 100644
--- a/test/outdated/outdated_test.dart
+++ b/test/outdated/outdated_test.dart
@@ -276,6 +276,46 @@
         environment: {'_PUB_TEST_SDK_VERSION': '2.13.0'});
   });
 
+  test('null-safety already migrated', () async {
+    await servePackages((builder) => builder
+      ..serve('foo', '1.0.0', pubspec: {
+        'environment': {'sdk': '>=2.9.0 < 3.0.0'}
+      })
+      ..serve('foo', '2.0.0', pubspec: {
+        'environment': {'sdk': '>=2.12.0 < 3.0.0'}
+      })
+      ..serve('bar', '1.0.0', pubspec: {
+        'environment': {'sdk': '>=2.9.0 < 3.0.0'}
+      })
+      ..serve('bar', '2.0.0', deps: {
+        'devTransitive': '^1.0.0'
+      }, pubspec: {
+        'environment': {'sdk': '>=2.12.0 < 3.0.0'}
+      })
+      ..serve('devTransitive', '1.0.0', pubspec: {
+        'environment': {'sdk': '>=2.9.0 < 3.0.0'}
+      }));
+
+    await d.dir(appPath, [
+      d.pubspec({
+        'name': 'app',
+        'version': '1.0.0',
+        'dependencies': {
+          'foo': '^2.0.0',
+        },
+        'dev_dependencies': {
+          'bar': '^2.0.0',
+        },
+        'environment': {'sdk': '>=2.12.0 < 3.0.0'},
+      }),
+    ]).create();
+
+    await pubGet(environment: {'_PUB_TEST_SDK_VERSION': '2.13.0'});
+
+    await variations('null_safety_already_migrated',
+        environment: {'_PUB_TEST_SDK_VERSION': '2.13.0'});
+  });
+
   test('overridden dependencies', () async {
     ensureGit();
     await servePackages(