diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index b6a3092..b0b41ac 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -21,7 +21,7 @@
         sdk: [dev]
     steps:
     - uses: actions/checkout@v2
-    - uses: dart-lang/setup-dart@v0.3
+    - uses: dart-lang/setup-dart@v1
       with:
         sdk: ${{ matrix.sdk }}
     - id: install
@@ -43,10 +43,10 @@
         sdk: [dev]
     steps:
     - uses: actions/checkout@v2
-    - uses: dart-lang/setup-dart@v0.3
+    - uses: dart-lang/setup-dart@v1
       with:
         sdk: ${{ matrix.sdk }}
-    - uses: nanasess/setup-chromedriver@v1.0.1
+    - uses: nanasess/setup-chromedriver@v1.0.5
     - id: install
       name: Install dependencies
       run: dart pub get
@@ -55,7 +55,7 @@
         export DISPLAY=:99
         chromedriver --port=4444 --url-base=/wd/hub &
         sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
-        pub run test --exclude-tags ff
+        dart test --exclude-tags ff
       if: always() && steps.install.outcome == 'success'
       env:
         CHROMEDRIVER_ARGS: '--no-sandbox --headless'
@@ -66,10 +66,10 @@
       fail-fast: false
       matrix:
         os: [ubuntu-latest]
-        sdk: [dev]
+        sdk: [2.14.0, dev]
     steps:
     - uses: actions/checkout@v2
-    - uses: dart-lang/setup-dart@v0.3
+    - uses: dart-lang/setup-dart@v1
       with:
         sdk: ${{ matrix.sdk }}
     - uses: browser-actions/setup-geckodriver@v0.0.0
@@ -81,5 +81,5 @@
         export DISPLAY=:99
         geckodriver --port=4445 &
         sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
-        pub run test --tags ff
+        dart test --tags ff
       if: always() && steps.install.outcome == 'success'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8415ddc..1ae76cf 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,10 +1,8 @@
-## 3.0.0-nullsafety.1
+## 3.0.1-dev
 
-- Make pedantic a dev_dependency.
+## 3.0.0
 
-## 3.0.0-nullsafety.0
-
-- Enable null safety.
+* Stable release for null safety.
 
 ## v2.1.2
 
diff --git a/LICENSE b/LICENSE
index 261eeb9..2b0fbbd 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
       same "printed page" as the copyright notice for easier
       identification within third-party archives.
 
-   Copyright [yyyy] [name of copyright owner]
+   Copyright 2013 Google LLC
 
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
diff --git a/analysis_options.yaml b/analysis_options.yaml
index a5a3a99..6488b3c 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,4 +1,4 @@
-include: package:pedantic/analysis_options.yaml
+include: package:lints/recommended.yaml
 
 analyzer:
   strong-mode:
diff --git a/lib/src/async/web_driver.dart b/lib/src/async/web_driver.dart
index 5a29d51..ebbcc91 100644
--- a/lib/src/async/web_driver.dart
+++ b/lib/src/async/web_driver.dart
@@ -24,7 +24,6 @@
 import '../common/utils.dart';
 import '../common/webdriver_handler.dart';
 import 'common.dart';
-
 // ignore: uri_does_not_exist
 import 'common_stub.dart'
 // ignore: uri_does_not_exist
@@ -136,7 +135,7 @@
   ///
   /// This is rather confusing and will be removed.
   /// Should replace all usages with [window.close()] or [quit()].
-  @deprecated
+  @Deprecated('Use `window.close()` or `quit()` instead.')
   Future<void> close() async => (await window).close();
 
   /// Handles for all of the currently displayed tabs/windows.
diff --git a/lib/src/async/web_element.dart b/lib/src/async/web_element.dart
index 2e2b5eb..8b60be8 100644
--- a/lib/src/async/web_element.dart
+++ b/lib/src/async/web_element.dart
@@ -132,7 +132,7 @@
   /// Access to the selenium attributes of this tag.
   ///
   /// This is deprecated, only used to support old pageloader.
-  @deprecated
+  @Deprecated('Only used to support the old page loader.')
   Attributes get seleniumAttributes => Attributes((name) => _client.send(
       _handler.element.buildSeleniumAttributeRequest(id, name),
       _handler.element.parseSeleniumAttributeResponse));
@@ -151,7 +151,7 @@
       _handler.element.parseCssPropertyResponse));
 
   Future<bool> equals(WebElement other) async =>
-      other is WebElement && other.driver == driver && other.id == id;
+      other.driver == driver && other.id == id;
 
   @override
   int get hashCode => driver.hashCode * 3 + id.hashCode;
@@ -169,12 +169,19 @@
       } else {
         out.write('.findElements(');
       }
-      out..write(locator)..write(')');
+      out
+        ..write(locator)
+        ..write(')');
     } else {
-      out..write('.')..write(locator);
+      out
+        ..write('.')
+        ..write(locator);
     }
     if (index != null) {
-      out..write('[')..write(index)..write(']');
+      out
+        ..write('[')
+        ..write(index)
+        ..write(']');
     }
     return out.toString();
   }
diff --git a/lib/src/common/webdriver_handler.dart b/lib/src/common/webdriver_handler.dart
index 2cc39cd..175fe0f 100644
--- a/lib/src/common/webdriver_handler.dart
+++ b/lib/src/common/webdriver_handler.dart
@@ -187,13 +187,13 @@
   /// Builds request for 'Selenium Element Attribute'.
   ///
   /// This is deprecated, only used to support old pageloader.
-  @deprecated
+  @Deprecated('Only used to support the old page loader.')
   WebDriverRequest buildSeleniumAttributeRequest(String elementId, String name);
 
   /// Parses response for 'Element Attribute'.
   ///
   /// This is deprecated, only used to support old pageloader.
-  @deprecated
+  @Deprecated('Only used to support the old page loader.')
   String? parseSeleniumAttributeResponse(WebDriverResponse response);
 
   /// Builds request for 'Element Property'.
diff --git a/lib/src/handler/json_wire/element.dart b/lib/src/handler/json_wire/element.dart
index 54b1438..a33b9e9 100644
--- a/lib/src/handler/json_wire/element.dart
+++ b/lib/src/handler/json_wire/element.dart
@@ -2,7 +2,6 @@
 
 import '../../common/request.dart';
 import '../../common/webdriver_handler.dart';
-
 import 'utils.dart';
 
 class JsonWireElementHandler extends ElementHandler {
@@ -121,13 +120,13 @@
       parseJsonWireResponse(response)?.toString();
 
   @override
-  @deprecated
+  @Deprecated('Only used to support the old page loader.')
   WebDriverRequest buildSeleniumAttributeRequest(
           String elementId, String name) =>
       WebDriverRequest.getRequest('${elementPrefix(elementId)}attribute/$name');
 
   @override
-  @deprecated
+  @Deprecated('Only used to support the old page loader.')
   String? parseSeleniumAttributeResponse(WebDriverResponse response) =>
       parseJsonWireResponse(response)?.toString();
 
diff --git a/lib/src/handler/json_wire/element_finder.dart b/lib/src/handler/json_wire/element_finder.dart
index 6a6e726..71ba1a3 100644
--- a/lib/src/handler/json_wire/element_finder.dart
+++ b/lib/src/handler/json_wire/element_finder.dart
@@ -9,10 +9,8 @@
       {'using': by.using, 'value': by.value};
 
   @override
-  WebDriverRequest buildFindElementsRequest(By by, [String? contextElementId]) {
-    var uri = contextElementId == null
-        ? 'elements'
-        : 'element/$contextElementId/elements';
+  WebDriverRequest buildFindElementsRequest(By by, [String? contextId]) {
+    var uri = contextId == null ? 'elements' : 'element/$contextId/elements';
     return WebDriverRequest.postRequest(uri, _byToJson(by));
   }
 
@@ -24,10 +22,8 @@
           .cast<String>();
 
   @override
-  WebDriverRequest buildFindElementRequest(By by, [String? contextElementId]) {
-    var uri = contextElementId == null
-        ? 'element'
-        : 'element/$contextElementId/element';
+  WebDriverRequest buildFindElementRequest(By by, [String? contextId]) {
+    var uri = contextId == null ? 'element' : 'element/$contextId/element';
     return WebDriverRequest.postRequest(uri, _byToJson(by));
   }
 
diff --git a/lib/src/handler/json_wire/utils.dart b/lib/src/handler/json_wire/utils.dart
index 8d4cd56..9d9caa1 100644
--- a/lib/src/handler/json_wire/utils.dart
+++ b/lib/src/handler/json_wire/utils.dart
@@ -23,9 +23,7 @@
 
   if (response.statusCode! < 200 ||
       response.statusCode! > 299 ||
-      (responseBody is Map &&
-          responseBody['status'] != null &&
-          responseBody['status'] != 0)) {
+      (responseBody['status'] != null && responseBody['status'] != 0)) {
     final status = responseBody['status'] as int?;
     final message = responseBody['value']['message'] as String?;
 
@@ -85,7 +83,7 @@
     }
   }
 
-  if (valueOnly && responseBody is Map) {
+  if (valueOnly) {
     return responseBody['value'];
   }
 
diff --git a/lib/src/handler/w3c/element.dart b/lib/src/handler/w3c/element.dart
index 13f21e8..3366de9 100644
--- a/lib/src/handler/w3c/element.dart
+++ b/lib/src/handler/w3c/element.dart
@@ -2,7 +2,6 @@
 
 import '../../common/request.dart';
 import '../../common/webdriver_handler.dart';
-
 import 'utils.dart';
 
 class W3cElementHandler extends ElementHandler {
@@ -116,13 +115,13 @@
       parseW3cResponse(response)?.toString();
 
   @override
-  @deprecated
+  @Deprecated('Only used to support the old page loader.')
   WebDriverRequest buildSeleniumAttributeRequest(
           String elementId, String name) =>
       WebDriverRequest.getRequest('${elementPrefix(elementId)}attribute/$name');
 
   @override
-  @deprecated
+  @Deprecated('Only used to support the old page loader.')
   String? parseSeleniumAttributeResponse(WebDriverResponse response) =>
       parseW3cResponse(response)?.toString();
 
diff --git a/lib/src/handler/w3c/element_finder.dart b/lib/src/handler/w3c/element_finder.dart
index d91a81f..2269ee2 100644
--- a/lib/src/handler/w3c/element_finder.dart
+++ b/lib/src/handler/w3c/element_finder.dart
@@ -39,8 +39,8 @@
   }
 
   @override
-  WebDriverRequest buildFindElementsRequest(By by, [String? contextElementId]) {
-    var uri = '${elementPrefix(contextElementId)}elements';
+  WebDriverRequest buildFindElementsRequest(By by, [String? contextId]) {
+    var uri = '${elementPrefix(contextId)}elements';
     return WebDriverRequest.postRequest(uri, _byToJson(by));
   }
 
@@ -51,8 +51,8 @@
           .toList();
 
   @override
-  WebDriverRequest buildFindElementRequest(By by, [String? contextElementId]) {
-    var uri = '${elementPrefix(contextElementId)}element';
+  WebDriverRequest buildFindElementRequest(By by, [String? contextId]) {
+    var uri = '${elementPrefix(contextId)}element';
     return WebDriverRequest.postRequest(uri, _byToJson(by));
   }
 
diff --git a/lib/src/sync/web_driver.dart b/lib/src/sync/web_driver.dart
index 004e306..f913a61 100644
--- a/lib/src/sync/web_driver.dart
+++ b/lib/src/sync/web_driver.dart
@@ -22,7 +22,6 @@
 import '../common/utils.dart';
 import '../common/webdriver_handler.dart';
 import 'common.dart';
-
 // ignore: uri_does_not_exist
 import 'common_stub.dart'
 // ignore: uri_does_not_exist
@@ -149,7 +148,7 @@
   ///
   /// This is rather confusing and will be removed.
   /// Should replace all usages with [window.close()] or [quit()].
-  @deprecated
+  @Deprecated('Use `window.close()` or `quit()` instead.')
   void close() {
     window.close();
   }
diff --git a/lib/src/sync/web_element.dart b/lib/src/sync/web_element.dart
index 122b91d..198b3bc 100644
--- a/lib/src/sync/web_element.dart
+++ b/lib/src/sync/web_element.dart
@@ -20,7 +20,6 @@
 import '../common/web_element.dart' as common;
 import '../common/webdriver_handler.dart';
 import 'common.dart';
-
 // ignore: uri_does_not_exist
 import 'common_stub.dart'
 // ignore: uri_does_not_exist
@@ -213,8 +212,7 @@
       _handler.element.parseCssPropertyResponse));
 
   /// Are these two elements the same underlying element in the DOM.
-  bool equals(WebElement other) =>
-      other is WebElement && other.driver == driver && other.id == id;
+  bool equals(WebElement other) => other.driver == driver && other.id == id;
 
   @override
   int get hashCode => driver.hashCode * 3 + id.hashCode;
@@ -232,12 +230,19 @@
       } else {
         out.write('.findElements(');
       }
-      out..write(locator)..write(')');
+      out
+        ..write(locator)
+        ..write(')');
     } else {
-      out..write('.')..write(locator);
+      out
+        ..write('.')
+        ..write(locator);
     }
     if (index != null) {
-      out..write('[')..write(index)..write(']');
+      out
+        ..write('[')
+        ..write(index)
+        ..write(']');
     }
     return out.toString();
   }
diff --git a/pubspec.yaml b/pubspec.yaml
index 4c74865..35820bb 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,23 +1,23 @@
 name: webdriver
-version: 3.0.0-nullsafety.1
+version: 3.0.1-dev
 
 description: >-
   Provides WebDriver bindings for Dart.
   Supports WebDriver JSON interface and W3C spec.
   Requires the use of WebDriver remote server.
 
-homepage: https://github.com/google/webdriver.dart
+repository: https://github.com/google/webdriver.dart
 
 environment:
-  sdk: '>=2.12.0-0 <3.0.0'
+  sdk: '>=2.14.0 <3.0.0'
 
 dependencies:
-  archive: ^3.0.0-nullsafety.0
-  matcher: ^0.12.10-nullsafety.0
-  path: ^1.8.0-nullsafety.0
-  stack_trace: ^1.10.0-nullsafety.0
-  sync_http: ^0.3.0-nullsafety.0
+  archive: ^3.0.0
+  matcher: ^0.12.10
+  path: ^1.8.0
+  stack_trace: ^1.10.0
+  sync_http: ^0.3.0
 
 dev_dependencies:
-  pedantic: ^1.10.0-nullsafety.0
-  test: ^1.16.0-nullsafety.0
+  lints: ^1.0.0
+  test: ^1.16.0
diff --git a/test/async_logs_test.dart b/test/async_logs_test.dart
index c301dce..3caa7bc 100644
--- a/test/async_logs_test.dart
+++ b/test/async_logs_test.dart
@@ -43,9 +43,9 @@
       }
 
       expect(logs, isNotEmpty);
-      logs.forEach((entry) {
+      for (var entry in logs) {
         expect(entry.level, equals(LogLevel.info));
-      });
+      }
     });
   }, timeout: const Timeout(Duration(minutes: 2)));
 }
diff --git a/test/async_window_test.dart b/test/async_window_test.dart
index a3f1568..d124590 100644
--- a/test/async_window_test.dart
+++ b/test/async_window_test.dart
@@ -19,8 +19,8 @@
 import 'dart:math' show Point, Rectangle;
 
 import 'package:test/test.dart';
-import 'package:webdriver/support/async.dart';
 import 'package:webdriver/async_core.dart';
+import 'package:webdriver/support/async.dart';
 
 import 'configs/async_io_config.dart' as config;
 
diff --git a/test/support/async_test.dart b/test/support/async_test.dart
index 5c9d78e..7f63323 100644
--- a/test/support/async_test.dart
+++ b/test/support/async_test.dart
@@ -14,9 +14,8 @@
 
 library webdriver.support.async_test;
 
-import 'dart:async' show Future;
+import 'dart:async' show Future, unawaited;
 
-import 'package:pedantic/pedantic.dart';
 import 'package:test/test.dart';
 import 'package:webdriver/support/async.dart';
 
diff --git a/test/support/firefox_profile_test.dart b/test/support/firefox_profile_test.dart
index 587cee0..ece45ef 100644
--- a/test/support/firefox_profile_test.dart
+++ b/test/support/firefox_profile_test.dart
@@ -133,11 +133,9 @@
 
       var expectedFiles = ['prefs.js', 'user.js'];
       expect(archive.files.length, greaterThanOrEqualTo(expectedFiles.length));
-      expectedFiles.forEach(
-        (f) => expect(
-          archive.files,
-          anyElement((ArchiveFile f) => f.name == 'prefs.js'),
-        ),
+      expect(
+        archive.files,
+        anyElement((ArchiveFile f) => f.name == 'prefs.js'),
       );
 
       var prefs = FirefoxProfile.loadPrefsFile(MockFile(
@@ -172,11 +170,9 @@
         'webapps/webapps.json'
       ];
       expect(archive.files.length, greaterThanOrEqualTo(expectedFiles.length));
-      expectedFiles.forEach(
-        (f) => expect(
-          archive.files,
-          anyElement((ArchiveFile f) => f.name == 'prefs.js'),
-        ),
+      expect(
+        archive.files,
+        anyElement((ArchiveFile f) => f.name == 'prefs.js'),
       );
 
       var prefs = FirefoxProfile.loadPrefsFile(
diff --git a/test/sync/logs.dart b/test/sync/logs.dart
index d5f3bcd..4ccbd22 100644
--- a/test/sync/logs.dart
+++ b/test/sync/logs.dart
@@ -45,9 +45,9 @@
       }
 
       expect(logs, isNotEmpty);
-      logs.forEach((entry) {
+      for (var entry in logs) {
         expect(entry.level, equals(LogLevel.info));
-      });
+      }
     });
   }, timeout: const Timeout(Duration(minutes: 2)));
 }