diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md
index f43d2fcc9b..3e2ec3f4c9 100644
--- a/script/tool/CHANGELOG.md
+++ b/script/tool/CHANGELOG.md
@@ -6,6 +6,7 @@
 - `xctest` now supports running macOS tests in addition to iOS
   - **Breaking change**: it now requires an `--ios` and/or `--macos` flag.
 - The tooling now runs in strong null-safe mode.
+- `publish plugins` check against pub.dev to determine if a release should happen.
 - Modified the output format of `pubspec-check` and `xctest`
 
 ## 0.2.0
diff --git a/script/tool/lib/src/publish_plugin_command.dart b/script/tool/lib/src/publish_plugin_command.dart
index 70ec75bc7b..622a1a3cb1 100644
--- a/script/tool/lib/src/publish_plugin_command.dart
+++ b/script/tool/lib/src/publish_plugin_command.dart
@@ -8,6 +8,7 @@ import 'dart:io' as io;
 
 import 'package:file/file.dart';
 import 'package:git/git.dart';
+import 'package:http/http.dart' as http;
 import 'package:meta/meta.dart';
 import 'package:path/path.dart' as p;
 import 'package:pub_semver/pub_semver.dart';
@@ -18,6 +19,7 @@ import 'common/core.dart';
 import 'common/git_version_finder.dart';
 import 'common/plugin_command.dart';
 import 'common/process_runner.dart';
+import 'common/pub_version_finder.dart';
 
 @immutable
 class _RemoteInfo {
@@ -49,7 +51,10 @@ class PublishPluginCommand extends PluginCommand {
     Print print = print,
     io.Stdin? stdinput,
     GitDir? gitDir,
-  })  : _print = print,
+    http.Client? httpClient,
+  })  : _pubVersionFinder =
+            PubVersionFinder(httpClient: httpClient ?? http.Client()),
+        _print = print,
         _stdin = stdinput ?? io.stdin,
         super(packagesDir, processRunner: processRunner, gitDir: gitDir) {
     argParser.addOption(
@@ -131,6 +136,7 @@ class PublishPluginCommand extends PluginCommand {
   final Print _print;
   final io.Stdin _stdin;
   StreamSubscription<String>? _stdinSubscription;
+  final PubVersionFinder _pubVersionFinder;
 
   @override
   Future<void> run() async {
@@ -182,6 +188,8 @@ class PublishPluginCommand extends PluginCommand {
         remoteForTagPush: remote,
       );
     }
+
+    _pubVersionFinder.httpClient.close();
     await _finish(successful);
   }
 
@@ -196,6 +204,7 @@ class PublishPluginCommand extends PluginCommand {
       _print('No version updates in this commit.');
       return true;
     }
+
     _print('Getting existing tags...');
     final io.ProcessResult existingTagsResult =
         await baseGitDir.runCommand(<String>['tag', '--sort=-committerdate']);
@@ -212,7 +221,6 @@ class PublishPluginCommand extends PluginCommand {
           .childFile(pubspecPath);
       final _CheckNeedsReleaseResult result = await _checkNeedsRelease(
         pubspecFile: pubspecFile,
-        gitVersionFinder: gitVersionFinder,
         existingTags: existingTags,
       );
       switch (result) {
@@ -271,7 +279,6 @@ class PublishPluginCommand extends PluginCommand {
   // Returns a [_CheckNeedsReleaseResult] that indicates the result.
   Future<_CheckNeedsReleaseResult> _checkNeedsRelease({
     required File pubspecFile,
-    required GitVersionFinder gitVersionFinder,
     required List<String> existingTags,
   }) async {
     if (!pubspecFile.existsSync()) {
@@ -293,19 +300,24 @@ Safe to ignore if the package is deleted in this commit.
       return _CheckNeedsReleaseResult.failure;
     }
 
-    // Get latest tagged version and compare with the current version.
-    // TODO(cyanglaz): Check latest version of the package on pub instead of git
-    // https://github.com/flutter/flutter/issues/81047
-
-    final String latestTag = existingTags.firstWhere(
-        (String tag) => tag.split('-v').first == pubspec.name,
-        orElse: () => '');
-    if (latestTag.isNotEmpty) {
-      final String latestTaggedVersion = latestTag.split('-v').last;
-      final Version latestVersion = Version.parse(latestTaggedVersion);
-      if (pubspec.version! < latestVersion) {
+    // Check if the package named `packageName` with `version` has already published.
+    final Version version = pubspec.version!;
+    final PubVersionFinderResponse pubVersionFinderResponse =
+        await _pubVersionFinder.getPackageVersion(package: pubspec.name);
+    if (pubVersionFinderResponse.versions.contains(version)) {
+      final String tagsForPackageWithSameVersion = existingTags.firstWhere(
+          (String tag) =>
+              tag.split('-v').first == pubspec.name &&
+              tag.split('-v').last == version.toString(),
+          orElse: () => '');
+      _print(
+          'The version $version of ${pubspec.name} has already been published');
+      if (tagsForPackageWithSameVersion.isEmpty) {
         _print(
-            'The new version (${pubspec.version}) is lower than the current version ($latestVersion) for ${pubspec.name}.\nThis git commit is a revert, no release is tagged.');
+            'However, the git release tag for this version (${pubspec.name}-v$version) is not found. Please manually fix the tag then run the command again.');
+        return _CheckNeedsReleaseResult.failure;
+      } else {
+        _print('skip.');
         return _CheckNeedsReleaseResult.noRelease;
       }
     }
diff --git a/script/tool/test/publish_plugin_command_test.dart b/script/tool/test/publish_plugin_command_test.dart
index a2ea9816ea..c7832e0da1 100644
--- a/script/tool/test/publish_plugin_command_test.dart
+++ b/script/tool/test/publish_plugin_command_test.dart
@@ -13,6 +13,8 @@ import 'package:flutter_plugin_tools/src/common/core.dart';
 import 'package:flutter_plugin_tools/src/common/process_runner.dart';
 import 'package:flutter_plugin_tools/src/publish_plugin_command.dart';
 import 'package:git/git.dart';
+import 'package:http/http.dart' as http;
+import 'package:http/testing.dart';
 import 'package:mockito/mockito.dart';
 import 'package:test/test.dart';
 
@@ -427,6 +429,37 @@ void main() {
     });
 
     test('can release newly created plugins', () async {
+      const Map<String, dynamic> httpResponsePlugin1 = <String, dynamic>{
+        'name': 'plugin1',
+        'versions': <String>[],
+      };
+
+      const Map<String, dynamic> httpResponsePlugin2 = <String, dynamic>{
+        'name': 'plugin2',
+        'versions': <String>[],
+      };
+
+      final MockClient mockClient = MockClient((http.Request request) async {
+        if (request.url.pathSegments.last == 'plugin1.json') {
+          return http.Response(json.encode(httpResponsePlugin1), 200);
+        } else if (request.url.pathSegments.last == 'plugin2.json') {
+          return http.Response(json.encode(httpResponsePlugin2), 200);
+        }
+        return http.Response('', 500);
+      });
+      final PublishPluginCommand command = PublishPluginCommand(packagesDir,
+          processRunner: processRunner,
+          print: (Object? message) => printedMessages.add(message.toString()),
+          stdinput: mockStdin,
+          httpClient: mockClient,
+          gitDir: gitDir);
+
+      commandRunner = CommandRunner<void>(
+        'publish_check_command',
+        'Test for publish-check command.',
+      );
+      commandRunner.addCommand(command);
+
       // Non-federated
       final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir);
       // federated
@@ -446,7 +479,6 @@ void main() {
           containsAllInOrder(<String>[
             'Checking local repo...',
             'Local repo is ready!',
-            'Getting existing tags...',
             'Running `pub publish ` in ${pluginDir1.path}...\n',
             'Running `pub publish ` in ${pluginDir2.path}...\n',
             'Packages released: plugin1, plugin2',
@@ -463,10 +495,50 @@ void main() {
 
     test('can release newly created plugins, while there are existing plugins',
         () async {
+      const Map<String, dynamic> httpResponsePlugin0 = <String, dynamic>{
+        'name': 'plugin0',
+        'versions': <String>['0.0.1'],
+      };
+
+      const Map<String, dynamic> httpResponsePlugin1 = <String, dynamic>{
+        'name': 'plugin1',
+        'versions': <String>[],
+      };
+
+      const Map<String, dynamic> httpResponsePlugin2 = <String, dynamic>{
+        'name': 'plugin2',
+        'versions': <String>[],
+      };
+
+      final MockClient mockClient = MockClient((http.Request request) async {
+        if (request.url.pathSegments.last == 'plugin0.json') {
+          return http.Response(json.encode(httpResponsePlugin0), 200);
+        } else if (request.url.pathSegments.last == 'plugin1.json') {
+          return http.Response(json.encode(httpResponsePlugin1), 200);
+        } else if (request.url.pathSegments.last == 'plugin2.json') {
+          return http.Response(json.encode(httpResponsePlugin2), 200);
+        }
+        return http.Response('', 500);
+      });
+      final PublishPluginCommand command = PublishPluginCommand(packagesDir,
+          processRunner: processRunner,
+          print: (Object? message) => printedMessages.add(message.toString()),
+          stdinput: mockStdin,
+          httpClient: mockClient,
+          gitDir: gitDir);
+
+      commandRunner = CommandRunner<void>(
+        'publish_check_command',
+        'Test for publish-check command.',
+      );
+      commandRunner.addCommand(command);
+
       // Prepare an exiting plugin and tag it
       createFakePlugin('plugin0', packagesDir);
       await gitDir.runCommand(<String>['add', '-A']);
       await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']);
+      await gitDir.runCommand(<String>['tag', 'plugin0-v0.0.1']);
+
       // Immediately return 0 when running `pub publish`.
       processRunner.mockPublishCompleteCode = 0;
       mockStdin.readLineOutput = 'y';
@@ -489,7 +561,6 @@ void main() {
           containsAllInOrder(<String>[
             'Checking local repo...',
             'Local repo is ready!',
-            'Getting existing tags...',
             'Running `pub publish ` in ${pluginDir1.path}...\n',
             'Running `pub publish ` in ${pluginDir2.path}...\n',
             'Packages released: plugin1, plugin2',
@@ -505,6 +576,36 @@ void main() {
     });
 
     test('can release newly created plugins, dry run', () async {
+      const Map<String, dynamic> httpResponsePlugin1 = <String, dynamic>{
+        'name': 'plugin1',
+        'versions': <String>[],
+      };
+
+      const Map<String, dynamic> httpResponsePlugin2 = <String, dynamic>{
+        'name': 'plugin2',
+        'versions': <String>[],
+      };
+
+      final MockClient mockClient = MockClient((http.Request request) async {
+        if (request.url.pathSegments.last == 'plugin1.json') {
+          return http.Response(json.encode(httpResponsePlugin1), 200);
+        } else if (request.url.pathSegments.last == 'plugin2.json') {
+          return http.Response(json.encode(httpResponsePlugin2), 200);
+        }
+        return http.Response('', 500);
+      });
+      final PublishPluginCommand command = PublishPluginCommand(packagesDir,
+          processRunner: processRunner,
+          print: (Object? message) => printedMessages.add(message.toString()),
+          stdinput: mockStdin,
+          httpClient: mockClient,
+          gitDir: gitDir);
+
+      commandRunner = CommandRunner<void>(
+        'publish_check_command',
+        'Test for publish-check command.',
+      );
+      commandRunner.addCommand(command);
       // Non-federated
       final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir);
       // federated
@@ -527,7 +628,6 @@ void main() {
             'Checking local repo...',
             'Local repo is ready!',
             '===============  DRY RUN ===============',
-            'Getting existing tags...',
             'Running `pub publish ` in ${pluginDir1.path}...\n',
             'Tagging release plugin1-v0.0.1...',
             'Pushing tag to upstream...',
@@ -541,6 +641,37 @@ void main() {
     });
 
     test('version change triggers releases.', () async {
+      const Map<String, dynamic> httpResponsePlugin1 = <String, dynamic>{
+        'name': 'plugin1',
+        'versions': <String>[],
+      };
+
+      const Map<String, dynamic> httpResponsePlugin2 = <String, dynamic>{
+        'name': 'plugin2',
+        'versions': <String>[],
+      };
+
+      final MockClient mockClient = MockClient((http.Request request) async {
+        if (request.url.pathSegments.last == 'plugin1.json') {
+          return http.Response(json.encode(httpResponsePlugin1), 200);
+        } else if (request.url.pathSegments.last == 'plugin2.json') {
+          return http.Response(json.encode(httpResponsePlugin2), 200);
+        }
+        return http.Response('', 500);
+      });
+      final PublishPluginCommand command = PublishPluginCommand(packagesDir,
+          processRunner: processRunner,
+          print: (Object? message) => printedMessages.add(message.toString()),
+          stdinput: mockStdin,
+          httpClient: mockClient,
+          gitDir: gitDir);
+
+      commandRunner = CommandRunner<void>(
+        'publish_check_command',
+        'Test for publish-check command.',
+      );
+      commandRunner.addCommand(command);
+
       // Non-federated
       final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir);
       // federated
@@ -558,7 +689,6 @@ void main() {
           containsAllInOrder(<String>[
             'Checking local repo...',
             'Local repo is ready!',
-            'Getting existing tags...',
             'Running `pub publish ` in ${pluginDir1.path}...\n',
             'Running `pub publish ` in ${pluginDir2.path}...\n',
             'Packages released: plugin1, plugin2',
@@ -600,7 +730,6 @@ void main() {
           containsAllInOrder(<String>[
             'Checking local repo...',
             'Local repo is ready!',
-            'Getting existing tags...',
             'Running `pub publish ` in ${pluginDir1.path}...\n',
             'Running `pub publish ` in ${pluginDir2.path}...\n',
             'Packages released: plugin1, plugin2',
@@ -619,6 +748,37 @@ void main() {
     test(
         'delete package will not trigger publish but exit the command successfully.',
         () async {
+      const Map<String, dynamic> httpResponsePlugin1 = <String, dynamic>{
+        'name': 'plugin1',
+        'versions': <String>[],
+      };
+
+      const Map<String, dynamic> httpResponsePlugin2 = <String, dynamic>{
+        'name': 'plugin2',
+        'versions': <String>[],
+      };
+
+      final MockClient mockClient = MockClient((http.Request request) async {
+        if (request.url.pathSegments.last == 'plugin1.json') {
+          return http.Response(json.encode(httpResponsePlugin1), 200);
+        } else if (request.url.pathSegments.last == 'plugin2.json') {
+          return http.Response(json.encode(httpResponsePlugin2), 200);
+        }
+        return http.Response('', 500);
+      });
+      final PublishPluginCommand command = PublishPluginCommand(packagesDir,
+          processRunner: processRunner,
+          print: (Object? message) => printedMessages.add(message.toString()),
+          stdinput: mockStdin,
+          httpClient: mockClient,
+          gitDir: gitDir);
+
+      commandRunner = CommandRunner<void>(
+        'publish_check_command',
+        'Test for publish-check command.',
+      );
+      commandRunner.addCommand(command);
+
       // Non-federated
       final Directory pluginDir1 = createFakePlugin('plugin1', packagesDir);
       // federated
@@ -636,7 +796,6 @@ void main() {
           containsAllInOrder(<String>[
             'Checking local repo...',
             'Local repo is ready!',
-            'Getting existing tags...',
             'Running `pub publish ` in ${pluginDir1.path}...\n',
             'Running `pub publish ` in ${pluginDir2.path}...\n',
             'Packages released: plugin1, plugin2',
@@ -677,7 +836,6 @@ void main() {
           containsAllInOrder(<String>[
             'Checking local repo...',
             'Local repo is ready!',
-            'Getting existing tags...',
             'Running `pub publish ` in ${pluginDir1.path}...\n',
             'The file at The pubspec file at ${pluginDir2.childFile('pubspec.yaml').path} does not exist. Publishing will not happen for plugin2.\nSafe to ignore if the package is deleted in this commit.\n',
             'Packages released: plugin1',
@@ -691,18 +849,48 @@ void main() {
       expect(processRunner.pushTagsArgs[2], 'plugin1-v0.0.2');
     });
 
-    test(
-        'versions revert do not trigger releases. Also prints out warning message.',
+    test('Exiting versions do not trigger release, also prints out message.',
         () async {
+      const Map<String, dynamic> httpResponsePlugin1 = <String, dynamic>{
+        'name': 'plugin1',
+        'versions': <String>['0.0.2'],
+      };
+
+      const Map<String, dynamic> httpResponsePlugin2 = <String, dynamic>{
+        'name': 'plugin2',
+        'versions': <String>['0.0.2'],
+      };
+
+      final MockClient mockClient = MockClient((http.Request request) async {
+        if (request.url.pathSegments.last == 'plugin1.json') {
+          return http.Response(json.encode(httpResponsePlugin1), 200);
+        } else if (request.url.pathSegments.last == 'plugin2.json') {
+          return http.Response(json.encode(httpResponsePlugin2), 200);
+        }
+        return http.Response('', 500);
+      });
+      final PublishPluginCommand command = PublishPluginCommand(packagesDir,
+          processRunner: processRunner,
+          print: (Object? message) => printedMessages.add(message.toString()),
+          stdinput: mockStdin,
+          httpClient: mockClient,
+          gitDir: gitDir);
+
+      commandRunner = CommandRunner<void>(
+        'publish_check_command',
+        'Test for publish-check command.',
+      );
+      commandRunner.addCommand(command);
+
       // Non-federated
-      final Directory pluginDir1 =
-          createFakePlugin('plugin1', packagesDir, version: '0.0.2');
+      createFakePlugin('plugin1', packagesDir, version: '0.0.2');
       // federated
-      final Directory pluginDir2 = createFakePlugin(
-          'plugin2', packagesDir.childDirectory('plugin2'),
+      createFakePlugin('plugin2', packagesDir.childDirectory('plugin2'),
           version: '0.0.2');
       await gitDir.runCommand(<String>['add', '-A']);
       await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']);
+      await gitDir.runCommand(<String>['tag', 'plugin1-v0.0.2']);
+      await gitDir.runCommand(<String>['tag', 'plugin2-v0.0.2']);
       // Immediately return 0 when running `pub publish`.
       processRunner.mockPublishCompleteCode = 0;
       mockStdin.readLineOutput = 'y';
@@ -713,54 +901,64 @@ void main() {
           containsAllInOrder(<String>[
             'Checking local repo...',
             'Local repo is ready!',
-            'Getting existing tags...',
-            'Running `pub publish ` in ${pluginDir1.path}...\n',
-            'Running `pub publish ` in ${pluginDir2.path}...\n',
-            'Packages released: plugin1, plugin2',
+            'The version 0.0.2 of plugin1 has already been published',
+            'skip.',
+            'The version 0.0.2 of plugin2 has already been published',
+            'skip.',
             'Done!'
           ]));
-      expect(processRunner.pushTagsArgs, isNotEmpty);
-      expect(processRunner.pushTagsArgs[0], 'push');
-      expect(processRunner.pushTagsArgs[1], 'upstream');
-      expect(processRunner.pushTagsArgs[2], 'plugin1-v0.0.2');
-      expect(processRunner.pushTagsArgs[3], 'push');
-      expect(processRunner.pushTagsArgs[4], 'upstream');
-      expect(processRunner.pushTagsArgs[5], 'plugin2-v0.0.2');
 
-      processRunner.pushTagsArgs.clear();
-      printedMessages.clear();
+      expect(processRunner.pushTagsArgs, isEmpty);
+    });
 
-      final List<String> plugin1Pubspec =
-          pluginDir1.childFile('pubspec.yaml').readAsLinesSync();
-      plugin1Pubspec[plugin1Pubspec.indexWhere(
-          (String element) => element.contains('version:'))] = 'version: 0.0.1';
-      pluginDir1
-          .childFile('pubspec.yaml')
-          .writeAsStringSync(plugin1Pubspec.join('\n'));
-      final List<String> plugin2Pubspec =
-          pluginDir2.childFile('pubspec.yaml').readAsLinesSync();
-      plugin2Pubspec[plugin2Pubspec.indexWhere(
-          (String element) => element.contains('version:'))] = 'version: 0.0.1';
-      pluginDir2
-          .childFile('pubspec.yaml')
-          .writeAsStringSync(plugin2Pubspec.join('\n'));
+    test(
+        'Exiting versions do not trigger release, but fail if the tags do not exist.',
+        () async {
+      const Map<String, dynamic> httpResponsePlugin1 = <String, dynamic>{
+        'name': 'plugin1',
+        'versions': <String>['0.0.2'],
+      };
+
+      const Map<String, dynamic> httpResponsePlugin2 = <String, dynamic>{
+        'name': 'plugin2',
+        'versions': <String>['0.0.2'],
+      };
+
+      final MockClient mockClient = MockClient((http.Request request) async {
+        if (request.url.pathSegments.last == 'plugin1.json') {
+          return http.Response(json.encode(httpResponsePlugin1), 200);
+        } else if (request.url.pathSegments.last == 'plugin2.json') {
+          return http.Response(json.encode(httpResponsePlugin2), 200);
+        }
+        return http.Response('', 500);
+      });
+      final PublishPluginCommand command = PublishPluginCommand(packagesDir,
+          processRunner: processRunner,
+          print: (Object? message) => printedMessages.add(message.toString()),
+          stdinput: mockStdin,
+          httpClient: mockClient,
+          gitDir: gitDir);
+
+      commandRunner = CommandRunner<void>(
+        'publish_check_command',
+        'Test for publish-check command.',
+      );
+      commandRunner.addCommand(command);
+
+      // Non-federated
+      createFakePlugin('plugin1', packagesDir, version: '0.0.2');
+      // federated
+      createFakePlugin('plugin2', packagesDir.childDirectory('plugin2'),
+          version: '0.0.2');
       await gitDir.runCommand(<String>['add', '-A']);
-      await gitDir
-          .runCommand(<String>['commit', '-m', 'Update versions to 0.0.1']);
-
-      await commandRunner
-          .run(<String>['publish-plugin', '--all-changed', '--base-sha=HEAD~']);
-      expect(
-          printedMessages,
-          containsAllInOrder(<String>[
-            'Checking local repo...',
-            'Local repo is ready!',
-            'Getting existing tags...',
-            'The new version (0.0.1) is lower than the current version (0.0.2) for plugin1.\nThis git commit is a revert, no release is tagged.',
-            'The new version (0.0.1) is lower than the current version (0.0.2) for plugin2.\nThis git commit is a revert, no release is tagged.',
-            'Done!'
-          ]));
-
+      await gitDir.runCommand(<String>['commit', '-m', 'Add plugins']);
+      // Immediately return 0 when running `pub publish`.
+      processRunner.mockPublishCompleteCode = 0;
+      mockStdin.readLineOutput = 'y';
+      await expectLater(
+          () => commandRunner.run(
+              <String>['publish-plugin', '--all-changed', '--base-sha=HEAD~']),
+          throwsA(const TypeMatcher<ToolExit>()));
       expect(processRunner.pushTagsArgs, isEmpty);
     });