Migrate the remainder of `package:intl`, excluding tests, to null safety.

PiperOrigin-RevId: 328490439
diff --git a/lib/date_symbol_data_custom.dart b/lib/date_symbol_data_custom.dart
index 45f91db..3da1abf 100644
--- a/lib/date_symbol_data_custom.dart
+++ b/lib/date_symbol_data_custom.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file for
 // details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 /// API to allow setting Date/Time formatting in a custom way.
 ///
@@ -19,7 +18,7 @@
 ///
 /// If data for this locale has already been initialized it will be overwritten.
 void initializeDateFormattingCustom(
-    {String locale, DateSymbols symbols, Map<String, String> patterns}) {
+    {String? locale, DateSymbols? symbols, Map<String, String>? patterns}) {
   initializeDateSymbols(_emptySymbols);
   initializeDatePatterns(_emptyPatterns);
   if (symbols == null) {
diff --git a/lib/date_symbol_data_file.dart b/lib/date_symbol_data_file.dart
index 620b987..6a4f33d 100644
--- a/lib/date_symbol_data_file.dart
+++ b/lib/date_symbol_data_file.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 /// This file should be imported, along with date_format.dart in order to read
 /// locale data from files in the file system.
diff --git a/lib/date_symbol_data_http_request.dart b/lib/date_symbol_data_http_request.dart
index 2c767db..0950856 100644
--- a/lib/date_symbol_data_http_request.dart
+++ b/lib/date_symbol_data_http_request.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 /// This file should be imported, along with date_format.dart in order to read
 /// locale data via http requests to a web server..
diff --git a/lib/date_symbol_data_local.dart b/lib/date_symbol_data_local.dart
index 4e7e7b6..c004257 100644
--- a/lib/date_symbol_data_local.dart
+++ b/lib/date_symbol_data_local.dart
@@ -3,7 +3,6 @@
 // for details. All rights reserved. Use of this source code
 // is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 /// Date/time formatting symbols for all locales.
 ///
@@ -30,10 +29,10 @@
 /// formatting methods are called. It sets up the lookup for date
 /// symbols. Both the [locale] and [ignored] parameter are ignored, as
 /// the data for all locales is directly available.
-Future<void> initializeDateFormatting([String locale, String ignored]) {
+Future<void> initializeDateFormatting([String? locale, String? ignored]) {
   initializeDateSymbols(dateTimeSymbolMap);
   initializeDatePatterns(dateTimePatternMap);
-  return new Future.value(null);
+  return new Future.value();
 }
 
 /// Returns a Map from locale names to the DateSymbols instance for
@@ -41,7 +40,7 @@
 /// instead.
 Map<dynamic, dynamic> dateTimeSymbolMap() => {
       // Date/time formatting symbols for locale en_ISO.
-      "en_ISO": new DateSymbols(
+      "en_ISO": DateSymbols(
           NAME: 'en_ISO',
           ERAS: const ['BC', 'AD'],
           ERANAMES: const ['Before Christ', 'Anno Domini'],
diff --git a/lib/date_time_patterns.dart b/lib/date_time_patterns.dart
index dfce6cf..162dc20 100644
--- a/lib/date_time_patterns.dart
+++ b/lib/date_time_patterns.dart
@@ -16,7 +16,7 @@
 /// Returns a Map from locale names to another Map that goes from skeletons
 /// to the locale-specific formatting patterns.
 /// Internal use only. Call initializeDateFormatting instead.
-Map<dynamic, dynamic> dateTimePatternMap() => const {
+Map<String, Map<String, String>> dateTimePatternMap() => const {
       /// Extended set of localized date/time patterns for locale af.
       'af': const {
         'd': 'd', // DAY
diff --git a/lib/intl.dart b/lib/intl.dart
index 7b0c6b9..0626561 100644
--- a/lib/intl.dart
+++ b/lib/intl.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 /// This library provides internationalization and localization. This includes
 /// message formatting and replacement, date and number formatting and parsing,
@@ -78,7 +77,7 @@
 class Intl {
   /// String indicating the locale code with which the message is to be
   /// formatted (such as en-CA).
-  String _locale;
+  final String _locale;
 
   /// The default locale. This defaults to being set from systemLocale, but
   /// can also be set explicitly, and will then apply to any new instances where
@@ -87,9 +86,9 @@
   /// will supercede this value while that operation is active. Using
   /// [Intl.withLocale] may be preferable if you are using different locales
   /// in the same application.
-  static String get defaultLocale => global_state.defaultLocale;
+  static String? get defaultLocale => global_state.defaultLocale;
 
-  static set defaultLocale(String newLocale) =>
+  static set defaultLocale(String? newLocale) =>
       global_state.defaultLocale = newLocale;
 
   /// The system's locale, as obtained from the window.navigator.language
@@ -101,7 +100,7 @@
 
   /// Return a new date format using the specified [pattern].
   /// If [desiredLocale] is not specified, then we default to [locale].
-  DateFormat date([String pattern, String desiredLocale]) {
+  DateFormat date([String? pattern, String? desiredLocale]) {
     var actualLocale = (desiredLocale == null) ? locale : desiredLocale;
     return DateFormat(pattern, actualLocale);
   }
@@ -110,9 +109,8 @@
   /// locale to be used, otherwise, we will attempt to infer it (acceptable if
   /// Dart is running on the client, we can infer from the browser/client
   /// preferences).
-  Intl([String aLocale]) {
-    _locale = aLocale != null ? aLocale : getCurrentLocale();
-  }
+  Intl([String? aLocale])
+      : _locale = aLocale != null ? aLocale : getCurrentLocale();
 
   /// Use this for a message that will be translated for different locales. The
   /// expected usage is that this is inside an enclosing function that only
@@ -166,19 +164,19 @@
   // messages, so it will eliminate the descriptions and other information
   // not needed at runtime.
   static String message(String messageText,
-          {String desc = '',
-          Map<String, Object> examples,
-          String locale,
-          String name,
-          List<Object> args,
-          String meaning,
-          bool skip}) =>
-      _message(messageText, locale, name, args, meaning);
+          {String? desc = '',
+          Map<String, Object>? examples,
+          String? locale,
+          String? name,
+          List<Object>? args,
+          String? meaning,
+          bool? skip}) =>
+      _message(messageText, locale, name, args, meaning)!;
 
   /// Omit the compile-time only parameters so dart2js can see to drop them.
   @pragma('dart2js:noInline')
-  static String _message(String messageText, String locale, String name,
-      List<Object> args, String meaning) {
+  static String? _message(String? messageText, String? locale, String? name,
+      List<Object>? args, String? meaning) {
     return helpers.messageLookup
         .lookupMessage(messageText, locale, name, args, meaning);
   }
@@ -203,8 +201,8 @@
   /// Note that null is interpreted as meaning the default locale, so if
   /// [newLocale] is null the default locale will be returned.
   static String verifiedLocale(
-          String newLocale, bool Function(String) localeExists,
-          {String Function(String) onFailure}) =>
+          String? newLocale, bool Function(String) localeExists,
+          {String Function(String)? onFailure}) =>
       helpers.verifiedLocale(newLocale, localeExists, onFailure);
 
   /// Return the short version of a locale name, e.g. 'en_US' => 'en'
@@ -214,7 +212,7 @@
   /// in the wrong case or with a hyphen instead of an underscore. If
   /// [aLocale] is null, for example, if you tried to get it from IE,
   /// return the current system locale.
-  static String canonicalizedLocale(String aLocale) =>
+  static String canonicalizedLocale(String? aLocale) =>
       helpers.canonicalizedLocale(aLocale);
 
   /// Formats a message differently depending on [howMany].
@@ -230,20 +228,20 @@
   @pragma('dart2js:tryInline')
   @pragma('vm:prefer-inline')
   static String plural(num howMany,
-      {String zero,
-      String one,
-      String two,
-      String few,
-      String many,
-      String other,
-      String desc,
-      Map<String, Object> examples,
-      String locale,
-      int precision,
-      String name,
-      List<Object> args,
-      String meaning,
-      bool skip}) {
+      {String? zero,
+      String? one,
+      String? two,
+      String? few,
+      String? many,
+      required String other,
+      String? desc,
+      Map<String, Object>? examples,
+      String? locale,
+      int? precision,
+      String? name,
+      List<Object>? args,
+      String? meaning,
+      bool? skip}) {
     // Call our internal method, dropping examples and desc because they're not
     // used at runtime and we want them to be optimized away.
     return _plural(howMany,
@@ -262,17 +260,17 @@
 
   @pragma('dart2js:noInline')
   static String _plural(num howMany,
-      {String zero,
-      String one,
-      String two,
-      String few,
-      String many,
-      String other,
-      String locale,
-      int precision,
-      String name,
-      List<Object> args,
-      String meaning}) {
+      {String? zero,
+      String? one,
+      String? two,
+      String? few,
+      String? many,
+      required String other,
+      String? locale,
+      int? precision,
+      String? name,
+      List<Object>? args,
+      String? meaning}) {
     // Look up our translation, but pass in a null message so we don't have to
     // eagerly evaluate calls that may not be necessary.
     var translated = _message(null, locale, name, args, meaning);
@@ -294,21 +292,17 @@
   /// Internal: Implements the logic for plural selection - use [plural] for
   /// normal messages.
   static T pluralLogic<T>(num howMany,
-      {T zero,
-      T one,
-      T two,
-      T few,
-      T many,
-      T other,
-      String locale,
-      int precision,
-      String meaning}) {
-    if (other == null) {
-      throw ArgumentError("The 'other' named argument must be provided");
-    }
-    if (howMany == null) {
-      throw ArgumentError('The howMany argument to plural cannot be null');
-    }
+      {T? zero,
+      T? one,
+      T? two,
+      T? few,
+      T? many,
+      required T other,
+      String? locale,
+      int? precision,
+      String? meaning}) {
+    ArgumentError.checkNotNull(other, 'other');
+    ArgumentError.checkNotNull(howMany, 'howMany');
     // If we haven't specified precision and we have a float that is an integer
     // value, turn it into an integer. This gives us the behavior that 1.0 and 1
     // produce the same output, e.g. 1 dollar.
@@ -355,21 +349,21 @@
     }
   }
 
-  static plural_rules.PluralRule _cachedPluralRule;
-  static String _cachedPluralLocale;
+  static plural_rules.PluralRule? _cachedPluralRule;
+  static String? _cachedPluralLocale;
 
   static plural_rules.PluralRule _pluralRule(
-      String locale, num howMany, int precision) {
+      String? locale, num howMany, int? precision) {
     plural_rules.startRuleEvaluation(howMany, precision);
     var verifiedLocale = Intl.verifiedLocale(
         locale, plural_rules.localeHasPluralRules,
         onFailure: (locale) => 'default');
     if (_cachedPluralLocale == verifiedLocale) {
-      return _cachedPluralRule;
+      return _cachedPluralRule!;
     } else {
       _cachedPluralRule = plural_rules.pluralRules[verifiedLocale];
       _cachedPluralLocale = verifiedLocale;
-      return _cachedPluralRule;
+      return _cachedPluralRule!;
     }
   }
 
@@ -377,16 +371,16 @@
   @pragma('dart2js:tryInline')
   @pragma('vm:prefer-inline')
   static String gender(String targetGender,
-      {String female,
-      String male,
-      String other,
-      String desc,
-      Map<String, Object> examples,
-      String locale,
-      String name,
-      List<Object> args,
-      String meaning,
-      bool skip}) {
+      {String? female,
+      String? male,
+      required String other,
+      String? desc,
+      Map<String, Object>? examples,
+      String? locale,
+      String? name,
+      List<Object>? args,
+      String? meaning,
+      bool? skip}) {
     // Call our internal method, dropping args and desc because they're not used
     // at runtime and we want them to be optimized away.
     return _gender(targetGender,
@@ -401,14 +395,13 @@
 
   @pragma('dart2js:noInline')
   static String _gender(String targetGender,
-      {String female,
-      String male,
-      String other,
-      String desc,
-      String locale,
-      String name,
-      List<Object> args,
-      String meaning}) {
+      {String? female,
+      String? male,
+      required String other,
+      String? locale,
+      String? name,
+      List<Object>? args,
+      String? meaning}) {
     // Look up our translation, but pass in a null message so we don't have to
     // eagerly evaluate calls that may not be necessary.
     var translated = _message(null, locale, name, args, meaning);
@@ -423,10 +416,8 @@
   /// Internal: Implements the logic for gender selection - use [gender] for
   /// normal messages.
   static T genderLogic<T>(String targetGender,
-      {T female, T male, T other, String locale}) {
-    if (other == null) {
-      throw ArgumentError("The 'other' named argument must be specified");
-    }
+      {T? female, T? male, required T other, String? locale}) {
+    ArgumentError.checkNotNull(other, 'other');
     switch (targetGender) {
       case 'female':
         return female == null ? other : female;
@@ -454,20 +445,20 @@
   @pragma('dart2js:tryInline')
   @pragma('vm:prefer-inline')
   static String select(Object choice, Map<Object, String> cases,
-      {String desc,
-      Map<String, Object> examples,
-      String locale,
-      String name,
-      List<Object> args,
-      String meaning,
-      bool skip}) {
+      {String? desc,
+      Map<String, Object>? examples,
+      String? locale,
+      String? name,
+      List<Object>? args,
+      String? meaning,
+      bool? skip}) {
     return _select(choice, cases,
         locale: locale, name: name, args: args, meaning: meaning);
   }
 
   @pragma('dart2js:noInline')
   static String _select(Object choice, Map<Object, String> cases,
-      {String locale, String name, List<Object> args, String meaning}) {
+      {String? locale, String? name, List<Object>? args, String? meaning}) {
     // Look up our translation, but pass in a null message so we don't have to
     // eagerly evaluate calls that may not be necessary.
     var stringChoice = choice is String ? choice : '$choice'.split('.').last;
@@ -524,7 +515,7 @@
   ///           desc: 'Say Hello');
   ///       Intl.withLocale('zh', Timer(Duration(milliseconds:10),
   ///           () => print(hello('World')));
-  static dynamic withLocale<T>(String locale, T Function() function) {
+  static dynamic withLocale<T>(String? locale, T Function() function) {
     // TODO(alanknight): Make this return T. This requires work because T might
     // be Future and the caller could get an unawaited Future.  Which is
     // probably an error in their code, but the change is semi-breaking.
@@ -536,8 +527,7 @@
   /// unless for some reason this gets called inside a message that resets the
   /// locale.
   static String getCurrentLocale() {
-    defaultLocale ??= systemLocale;
-    return defaultLocale;
+    return defaultLocale ??= systemLocale;
   }
 
   String toString() => 'Intl($locale)';
@@ -550,7 +540,7 @@
 /// many locales, and we have the option to extend this to handle more cases
 /// without changing the API for clients. It also hard-codes the case of
 /// dotted i in Turkish and Azeri.
-String toBeginningOfSentenceCase(String input, [String locale]) {
+String? toBeginningOfSentenceCase(String? input, [String? locale]) {
   if (input == null || input.isEmpty) return input;
   return '${_upperCaseLetter(input[0], locale)}${input.substring(1)}';
 }
@@ -564,7 +554,7 @@
 // See http://www.unicode.org/Public/UNIDATA/SpecialCasing.txt
 // TODO(alanknight): Alternatively, consider toLocaleUpperCase in browsers.
 // See also https://github.com/dart-lang/sdk/issues/6706
-String _upperCaseLetter(String input, String locale) {
+String _upperCaseLetter(String input, String? locale) {
   // Hard-code the important edge case of i->İ
   if (locale != null) {
     if (input == 'i' && locale.startsWith('tr') || locale.startsWith('az')) {
diff --git a/lib/intl_browser.dart b/lib/intl_browser.dart
index 3a9db49..28d45ed 100644
--- a/lib/intl_browser.dart
+++ b/lib/intl_browser.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 /// This provides facilities for Internationalization that are only available
 /// when running in the web browser. You should import only one of this or
diff --git a/lib/intl_standalone.dart b/lib/intl_standalone.dart
index 626ac22..90563fc 100644
--- a/lib/intl_standalone.dart
+++ b/lib/intl_standalone.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 /// This provides facilities for Internationalization that are only available
 /// when running standalone. You should import only one of this or
diff --git a/lib/locale.dart b/lib/locale.dart
index ea24a87..14b65d0 100644
--- a/lib/locale.dart
+++ b/lib/locale.dart
@@ -1,3 +1,2 @@
 /// This library provides access to the [Locale] class.
-// @dart=2.9
 export 'src/locale.dart';
diff --git a/lib/message_format.dart b/lib/message_format.dart
index be010fa..45b47ef 100644
--- a/lib/message_format.dart
+++ b/lib/message_format.dart
@@ -1,9 +1,8 @@
 // Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
-/// `MessageFormat` is a "locale aware printf", with plural / gender support.
+// `MessageFormat` is a "locale aware printf", with plural / gender support.
 ///
 /// `MessageFormat` prepares strings for display to users, with optional
 /// arguments (variables/placeholders). The arguments can occur in any order,
@@ -109,22 +108,22 @@
 class MessageFormat {
   /// The locale to use for plural, ordinal, decisions,
   /// number / date / time formatting
-  String _locale;
+  final String _locale;
 
   /// The pattern we parse and apply positional parameters to.
-  String _pattern;
+  String? _pattern;
 
   /// All encountered literals during parse stage.
-  Queue<String> _initialLiterals;
+  Queue<String>? _initialLiterals;
 
   /// Working list with all encountered literals during parse and format stages.
-  Queue<String> _literals;
+  Queue<String>? _literals;
 
   /// Input pattern gets parsed into objects for faster formatting.
-  Queue<_BlockTypeAndVal> _parsedPattern;
+  Queue<_BlockTypeAndVal>? _parsedPattern;
 
   /// Locale aware number formatter.
-  NumberFormat _numberFormat;
+  final NumberFormat _numberFormat;
 
   /// Literal strings, including '', are replaced with \uFDDF_x_ for parsing.
   ///
@@ -146,11 +145,10 @@
   /// It does parameter substitutions in a locale-aware way.
   /// The syntax is similar to the one used by ICU and is described in the
   /// grammar above.
-  MessageFormat(String pattern, {String locale = 'en'}) {
-    _locale = locale;
-    _pattern = pattern;
-    _numberFormat = NumberFormat.decimalPattern(locale);
-  }
+  MessageFormat(String pattern, {String locale = 'en'})
+      : _locale = locale,
+        _pattern = pattern,
+        _numberFormat = NumberFormat.decimalPattern(locale);
 
   /// Returns a formatted message, treating '#' as a special placeholder.
   ///
@@ -163,7 +161,7 @@
   /// `NUM_PEOPLE` parameter could mean 5 people, which could influence plural
   /// format, and `NAME` parameter is just a data to be printed out in proper
   /// position.
-  String format([Map<String, Object> namedParameters]) {
+  String format([Map<String, Object>? namedParameters]) {
     return _format(false, namedParameters);
   }
 
@@ -176,7 +174,7 @@
   /// `NUM_PEOPLE` parameter could mean 5 people, which could influence plural
   /// format, and `NAME` parameter is just a data to be printed out in proper
   /// position.
-  String formatIgnoringPound([Map<String, Object> namedParameters]) {
+  String formatIgnoringPound([Map<String, Object>? namedParameters]) {
     return _format(true, namedParameters);
   }
 
@@ -192,13 +190,13 @@
   /// If [ignorePound] is true, treat '#' in plural messages as a
   /// literal character, else treat it as an ICU syntax character, resolving
   /// to the number (plural_variable - offset).
-  String _format(bool ignorePound, [Map<String, Object> namedParameters]) {
+  String _format(bool ignorePound, [Map<String, Object>? namedParameters]) {
     _init();
-    if (_parsedPattern == null || _parsedPattern.isEmpty) {
+    if (_parsedPattern == null || _parsedPattern!.isEmpty) {
       return '';
     }
     // Clone, we don't want to damage the original
-    _literals = Queue<String>()..addAll(_initialLiterals);
+    _literals = Queue<String>()..addAll(_initialLiterals!);
 
     // Implementation notes: this seems inefficient, we could in theory do the
     // replace + join in one go.
@@ -221,16 +219,16 @@
     // If we think printf, how many arguments are common?
     // Probably less than 5 or so.
     var messageParts = Queue<String>();
-    _formatBlock(_parsedPattern, namedParameters, ignorePound, messageParts);
+    _formatBlock(_parsedPattern!, namedParameters!, ignorePound, messageParts);
     var message = messageParts.join('');
 
     if (!ignorePound) {
       _checkAndThrow(!message.contains('#'), 'Not all # were replaced.');
     }
 
-    while (_literals.isNotEmpty) {
+    while (_literals!.isNotEmpty) {
       message = message.replaceFirst(
-          _buildPlaceholder(_literals), _literals.removeLast());
+          _buildPlaceholder(_literals!), _literals!.removeLast());
     }
 
     return message;
@@ -259,24 +257,25 @@
           'The type should be a block type: $patternType');
       switch (patternType) {
         case _BlockType.string:
-          result.add(patternValue);
+          result.add(patternValue as String);
           break;
         case _BlockType.simple:
-          _formatSimplePlaceholder(patternValue, namedParameters, result);
+          _formatSimplePlaceholder(
+              patternValue as String, namedParameters, result);
           break;
         case _BlockType.select:
           _checkAndThrow(patternValue is Map<String, Object>,
               'The value should be a map: $patternValue');
-          Map<String, Object> mapPattern = patternValue;
+          var mapPattern = patternValue as Map<String, Object>;
           _formatSelectBlock(mapPattern, namedParameters, ignorePound, result);
           break;
         case _BlockType.plural:
-          _formatPluralOrdinalBlock(patternValue, namedParameters,
-              _PluralRules.select, ignorePound, result);
+          _formatPluralOrdinalBlock(patternValue as Map<String, Object>,
+              namedParameters, _PluralRules.select, ignorePound, result);
           break;
         case _BlockType.ordinal:
-          _formatPluralOrdinalBlock(patternValue, namedParameters,
-              _OrdinalRules.select, ignorePound, result);
+          _formatPluralOrdinalBlock(patternValue as Map<String, Object>,
+              namedParameters, _OrdinalRules.select, ignorePound, result);
           break;
         default:
           _checkAndThrow(false, 'Unrecognized block type: $patternType');
@@ -307,8 +306,8 @@
     } else {
       strValue = value.toString();
     }
-    _literals.add(strValue);
-    result.add(_buildPlaceholder(_literals));
+    _literals!.add(strValue);
+    result.add(_buildPlaceholder(_literals!));
   }
 
   /// Formats select block. Only one option is selected.
@@ -331,14 +330,15 @@
       return;
     }
 
-    var option = parsedBlocks[namedParameters[argumentName]];
+    var option =
+        parsedBlocks[namedParameters[argumentName]] as Queue<_BlockTypeAndVal>?;
     if (!_isDef(option)) {
-      option = parsedBlocks[_other];
+      option = parsedBlocks[_other] as Queue<_BlockTypeAndVal>?;
       _checkAndThrow(option != null,
           'Invalid option or missing other option for select block.');
     }
 
-    _formatBlock(option, namedParameters, ignorePound, result);
+    _formatBlock(option!, namedParameters, ignorePound, result);
   }
 
   /// Formats `plural` / `selectordinal` block, selects an option, replaces `#`
@@ -377,7 +377,7 @@
 
     var numArgumentOffset = argumentOffset is num
         ? argumentOffset
-        : double.tryParse(argumentOffset);
+        : double.tryParse(argumentOffset as String);
     if (numArgumentOffset == null) {
       result.add('Invalid offset - $argumentOffset');
       return;
@@ -386,19 +386,21 @@
     var diff = numPluralValue - numArgumentOffset;
 
     // Check if there is an exact match.
-    var option = parsedBlocks[namedParameters[argumentName]];
+    var option =
+        parsedBlocks[namedParameters[argumentName]] as Queue<_BlockTypeAndVal>?;
     if (!_isDef(option)) {
-      option = parsedBlocks[namedParameters[argumentName].toString()];
+      option = parsedBlocks[namedParameters[argumentName].toString()]
+          as Queue<_BlockTypeAndVal>?;
     }
     if (!_isDef(option)) {
       var item = pluralSelector(diff.abs(), _locale);
       _checkAndThrow(item is String, 'Invalid plural key.');
 
-      option = parsedBlocks[item];
+      option = parsedBlocks[item] as Queue<_BlockTypeAndVal>?;
 
       // If option is not provided fall back to "other".
       if (!_isDef(option)) {
-        option = parsedBlocks[_other];
+        option = parsedBlocks[_other] as Queue<_BlockTypeAndVal>?;
       }
 
       _checkAndThrow(option != null,
@@ -406,7 +408,7 @@
     }
 
     var pluralResult = Queue<String>();
-    _formatBlock(option, namedParameters, ignorePound, pluralResult);
+    _formatBlock(option!, namedParameters, ignorePound, pluralResult);
     var plural = pluralResult.join('');
     _checkAndThrow(plural is String, 'Empty block in plural.');
     if (ignorePound) {
@@ -425,7 +427,7 @@
   void _init() {
     if (_pattern != null) {
       _initialLiterals = Queue<String>();
-      var pattern = _insertPlaceholders(_pattern);
+      var pattern = _insertPlaceholders(_pattern!);
 
       _parsedPattern = _parseBlock(pattern);
       _pattern = null;
@@ -438,7 +440,7 @@
   /// set of characters not containing '
   /// Builds a dictionary so we can recover literals during format phase.
   String _insertPlaceholders(String pattern) {
-    var literals = _initialLiterals;
+    var literals = _initialLiterals!;
     var buildPlaceholder = _buildPlaceholder;
 
     // First replace '' with single quote placeholder since they can be found
@@ -450,7 +452,7 @@
 
     pattern = pattern.replaceAllMapped(_regexLiteral, (match) {
       // match, text
-      var text = match.group(1);
+      var text = match.group(1)!;
       literals.add(text);
       return buildPlaceholder(literals);
     });
@@ -470,7 +472,7 @@
     for (match in braces.allMatches(pattern)) {
       var pos = match.start;
       if (match[0] == '}') {
-        String brace;
+        String? brace;
         try {
           brace = braceStack.removeLast();
         } on StateError {
@@ -556,7 +558,7 @@
     var result = Queue<_BlockTypeAndVal>();
     var parts = _extractParts(pattern);
     for (var thePart in parts) {
-      _BlockTypeAndVal block;
+      _BlockTypeAndVal? block;
       if (_ElementType.string == thePart._type) {
         block = _BlockTypeAndVal(_BlockType.string, thePart._value);
       } else if (_ElementType.block == thePart._type) {
@@ -587,7 +589,7 @@
       } else {
         _checkAndThrow(false, 'Unknown part of the pattern.');
       }
-      result.add(block);
+      result.add(block!);
     }
 
     return result;
@@ -602,7 +604,7 @@
     var replaceRegex = _selectBlockRe;
     pattern = pattern.replaceFirstMapped(replaceRegex, (match) {
       // string, name
-      argumentName = match.group(1);
+      argumentName = match.group(1)!;
       return '';
     });
     var result = <String, Object>{'argumentName': argumentName};
@@ -620,13 +622,13 @@
           pos < parts.length, 'Missing or invalid select value element.');
       thePart = parts.elementAt(pos);
 
-      Queue<_BlockTypeAndVal> value;
+      Queue<_BlockTypeAndVal>? value;
       if (_ElementType.block == thePart._type) {
         value = _parseBlock(thePart._value);
       } else {
         _checkAndThrow(false, 'Expected block type.');
       }
-      result[key.replaceAll(RegExp('\\s'), '')] = value;
+      result[key.replaceAll(RegExp('\\s'), '')] = value!;
       pos++;
     }
 
@@ -645,9 +647,9 @@
     var replaceRegex = _pluralBlockRe;
     pattern = pattern.replaceFirstMapped(replaceRegex, (match) {
       // string, name, offset
-      argumentName = match.group(1);
+      argumentName = match.group(1)!;
       if (_isDef(match.group(2))) {
-        argumentOffset = int.parse(match.group(2));
+        argumentOffset = int.parse(match.group(2)!);
       }
       return '';
     });
@@ -670,7 +672,7 @@
           pos < parts.length, 'Missing or invalid plural value element.');
       thePart = parts.elementAt(pos);
 
-      Queue<_BlockTypeAndVal> value;
+      Queue<_BlockTypeAndVal>? value;
       if (_ElementType.block == thePart._type) {
         value = _parseBlock(thePart._value);
       } else {
@@ -679,7 +681,7 @@
       key = key.replaceFirstMapped(RegExp('\\s*(?:=)?(\\w+)\\s*'), (match) {
         return match.group(1).toString();
       });
-      result[key] = value;
+      result[key] = value!;
       pos++;
     }
 
@@ -709,7 +711,7 @@
     var replaceRegex = _ordinalBlockRe;
     pattern = pattern.replaceFirstMapped(replaceRegex, (match) {
       // string, name
-      argumentName = match.group(1);
+      argumentName = match.group(1)!;
       return '';
     });
 
@@ -728,7 +730,7 @@
           pos < parts.length, 'Missing or invalid ordinal value element.');
       thePart = parts.elementAt(pos);
 
-      Queue<_BlockTypeAndVal> value;
+      Queue<_BlockTypeAndVal>? value;
       if (_ElementType.block == thePart._type) {
         value = _parseBlock(thePart._value);
       } else {
@@ -737,7 +739,7 @@
       key = key.replaceFirstMapped(RegExp('\\s*(?:=)?(\\w+)\\s*'), (match) {
         return match.group(1).toString();
       });
-      result[key] = value;
+      result[key] = value!;
       pos++;
     }
 
@@ -762,7 +764,7 @@
 //========== EXTRAS: temporary, to help the move from JS to Dart ==========
 
 // Simple goog.isDef replacement, will probably remove it
-bool _isDef(Object obj) {
+bool _isDef(Object? obj) {
   return obj != null;
 }
 
diff --git a/lib/message_lookup_by_library.dart b/lib/message_lookup_by_library.dart
index a8d83be..734033c 100644
--- a/lib/message_lookup_by_library.dart
+++ b/lib/message_lookup_by_library.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 /// Message/plural format library with locale support. This can have different
 /// implementations based on the mechanism for finding the localized versions of
@@ -29,17 +28,17 @@
   ///
   ///  If this locale matches the new one then we can skip looking up the
   ///  messages and assume they will be the same as last time.
-  String _lastLocale;
+  String? _lastLocale;
 
   /// Caches the last messages that we found
-  MessageLookupByLibrary _lastLookup;
+  MessageLookupByLibrary? _lastLookup;
 
   /// Look up the message with the given [name] and [locale] and return the
   /// translated version with the values in [args] interpolated.  If nothing is
   /// found, return the result of [ifAbsent] or [messageText].
-  String lookupMessage(String messageText, String locale, String name,
-      List<dynamic> args, String meaning,
-      {MessageIfAbsent ifAbsent}) {
+  String? lookupMessage(String? messageText, String? locale, String? name,
+      List<Object>? args, String? meaning,
+      {MessageIfAbsent? ifAbsent}) {
     // If passed null, use the default.
     var knownLocale = locale ?? Intl.getCurrentLocale();
     var messages = (knownLocale == _lastLocale)
@@ -55,7 +54,7 @@
   }
 
   /// Find the right message lookup for [locale].
-  MessageLookupByLibrary _lookupMessageCatalog(String locale) {
+  MessageLookupByLibrary? _lookupMessageCatalog(String locale) {
     var verifiedLocale = Intl.verifiedLocale(locale, localeExists,
         onFailure: (locale) => locale);
     _lastLocale = locale;
@@ -98,15 +97,15 @@
   /// Ultimately, the information about the enclosing function and its arguments
   /// will be extracted automatically but for the time being it must be passed
   /// explicitly in the [name] and [args] arguments.
-  String lookupMessage(String messageText, String locale, String name,
-      List<dynamic> args, String meaning,
-      {MessageIfAbsent ifAbsent}) {
-    var notFound = false;
+  String? lookupMessage(String? messageText, String? locale, String? name,
+      List<Object>? args, String? meaning,
+      {MessageIfAbsent? ifAbsent}) {
     var actualName = computeMessageName(name, messageText, meaning);
-    if (actualName == null) notFound = true;
-    var translation = this[actualName];
-    notFound = notFound || (translation == null);
-    if (notFound) {
+    Object? translation;
+    if (actualName != null) {
+      translation = this[actualName];
+    }
+    if (translation == null) {
       return ifAbsent == null ? messageText : ifAbsent(messageText, args);
     } else {
       args = args ?? const [];
diff --git a/lib/src/file_data_reader.dart b/lib/src/file_data_reader.dart
index d64e0f0..10b6edc 100644
--- a/lib/src/file_data_reader.dart
+++ b/lib/src/file_data_reader.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 /// This contains a reader that accesses data from local files, so it can't
 /// be run in the browser.
diff --git a/lib/src/http_request_data_reader.dart b/lib/src/http_request_data_reader.dart
index 2155f43..eff31b5 100644
--- a/lib/src/http_request_data_reader.dart
+++ b/lib/src/http_request_data_reader.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 /// This contains a reader that accesses data using the HttpRequest
 /// facility, and thus works only in the web browser.
@@ -20,7 +19,7 @@
   Future<String> read(String locale) {
     var request = HttpRequest();
     request.timeout = 5000;
-    return _getString('$url$locale.json', request).then((r) => r.responseText);
+    return _getString('$url$locale.json', request).then((r) => r.responseText!);
   }
 
   /// Read a string with the given request. This is a stripped down copy
@@ -31,7 +30,7 @@
     xhr.open('GET', url, async: true);
     xhr.onLoad.listen((e) {
       // Note: file:// URIs have status of 0.
-      if ((xhr.status >= 200 && xhr.status < 300) ||
+      if ((xhr.status! >= 200 && xhr.status! < 300) ||
           xhr.status == 0 ||
           xhr.status == 304) {
         completer.complete(xhr);
diff --git a/lib/src/intl/compact_number_format.dart b/lib/src/intl/compact_number_format.dart
index 99f5280..03108d3 100644
--- a/lib/src/intl/compact_number_format.dart
+++ b/lib/src/intl/compact_number_format.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 part of 'number_format.dart';
 
@@ -74,7 +73,7 @@
   /// The pattern on which this is based.
   ///
   /// We don't actually need this, but it makes debugging easier.
-  String pattern;
+  String? pattern;
 
   /// The normalized scientific notation exponent for which the format applies.
   ///
@@ -126,7 +125,7 @@
   /// workaround for locales like 'it', which include patterns with no suffix
   /// for numbers >= 1000 but < 1,000,000.
   bool get printsAsIs =>
-      isFallback || pattern.replaceAll(RegExp('[0\u00a0\u00a4]'), '').isEmpty;
+      isFallback || pattern!.replaceAll(RegExp('[0\u00a0\u00a4]'), '').isEmpty;
 
   _CompactStyle styleForSign(number) => this;
   List<_CompactStyle> get allStyles => [this];
@@ -142,9 +141,9 @@
   /// Creates a [_CompactStyle] instance for pattern with [normalizedExponent].
   static _CompactStyle createStyle(String pattern, int normalizedExponent) {
     var match = _regex.firstMatch(pattern);
-    var integerDigits = match.group(2).length;
-    var prefix = match.group(1);
-    var suffix = match.group(3);
+    var integerDigits = match!.group(2)!.length;
+    var prefix = match.group(1)!;
+    var suffix = match.group(3)!;
     // If the pattern is just zeros, with no suffix, then we shouldn't divide
     // by the number of digits. e.g. for 'af', the pattern for 3 is '0', but
     // it doesn't mean that 4321 should print as 4. But if the pattern was
@@ -153,7 +152,7 @@
     // encode that. Check what other things are doing.
     var divisor = 1;
     if (_hasNonZeroContent(pattern)) {
-      divisor = pow(10, normalizedExponent - integerDigits + 1);
+      divisor = pow(10, normalizedExponent - integerDigits + 1) as int;
     }
     return _CompactStyle(
         pattern: pattern,
@@ -179,18 +178,18 @@
   final List<_CompactStyleBase> _styles;
 
   factory _CompactNumberFormat(
-      {String locale,
-      _CompactFormatType formatType,
-      String name,
-      String currencySymbol,
-      String Function(NumberSymbols) getPattern = _forDecimal,
-      int decimalDigits,
+      {String? locale,
+      _CompactFormatType? formatType,
+      String? name,
+      String? currencySymbol,
+      String? Function(NumberSymbols) getPattern = _forDecimal,
+      int? decimalDigits,
       bool lookupSimpleCurrencySymbol = false,
       bool isForCurrency = false}) {
     // Initialization copied from `NumberFormat` constructor.
     // TODO(davidmorgan): deduplicate.
-    locale = Intl.verifiedLocale(locale, NumberFormat.localeExists);
-    var symbols = numberFormatSymbols[locale];
+    locale = helpers.verifiedLocale(locale, NumberFormat.localeExists, null);
+    var symbols = numberFormatSymbols[locale] as NumberSymbols;
     var localeZero = symbols.ZERO_DIGIT.codeUnitAt(0);
     var zeroOffset = localeZero - constants.asciiZeroCodeUnit;
     name ??= symbols.DEF_CURRENCY_CODE;
@@ -212,7 +211,7 @@
     /// or COMPACT_DECIMAL_SHORT_CURRENCY_PATTERN members.
     Map<int, String> patterns;
 
-    var compactSymbols = compactNumberSymbols[locale];
+    var compactSymbols = compactNumberSymbols[locale]!;
 
     var styles = <_CompactStyleBase>[];
     switch (formatType) {
@@ -267,7 +266,7 @@
       bool isForCurrency,
       String locale,
       int localeZero,
-      String pattern,
+      String? pattern,
       NumberSymbols symbols,
       int zeroOffset,
       NumberFormatParseResult result,
@@ -282,21 +281,22 @@
   /// The style in which we will format a particular number.
   ///
   /// This is a temporary variable that is only valid within a call to format.
-  _CompactStyle _style;
+  _CompactStyle? _style;
 
   String format(number) {
-    _style = _styleFor(number);
-    final divisor = _style.printsAsIs ? 1 : _style.divisor;
+    var style = _styleFor(number);
+    _style = style;
+    final divisor = style.printsAsIs ? 1 : style.divisor;
     final numberToFormat = _divide(number, divisor);
     var formatted = super.format(numberToFormat);
-    var prefix = _style.prefix;
-    var suffix = _style.suffix;
+    var prefix = style.prefix;
+    var suffix = style.suffix;
     // If this is for a currency, then the super call will have put the currency
     // somewhere. We don't want it there, we want it where our style indicates,
     // so remove it and replace. This has the remote possibility of a false
     // positive, but it seems unlikely that e.g. USD would occur as a string in
     // a regular number.
-    if (_isForCurrency && !_style.isFallback) {
+    if (_isForCurrency && !style.isFallback) {
       formatted = formatted.replaceFirst(currencySymbol, '').trim();
       prefix = prefix.replaceFirst('\u00a4', currencySymbol);
       suffix = suffix.replaceFirst('\u00a4', currencySymbol);
@@ -315,14 +315,14 @@
     // compact, always use the number of significant digits and ignore
     // decimalDigits. That is, $1.23K but also ¥12.3\u4E07, even though yen
     // don't normally print decimal places.
-    if (!_isForCurrency || !_style.isFallback) return newFractionDigits;
+    if (!_isForCurrency || !_style!.isFallback) return newFractionDigits;
     // If we are printing a currency and it's too small to compact, but
     // significant digits would have us only print some of the decimal digits,
     // use all of them. So $12.30, not $12.3
-    if (newFractionDigits > 0 && newFractionDigits < decimalDigits) {
-      return decimalDigits;
+    if (newFractionDigits > 0 && newFractionDigits < decimalDigits!) {
+      return decimalDigits!;
     } else {
-      return min(newFractionDigits, decimalDigits);
+      return min(newFractionDigits, decimalDigits!);
     }
   }
 
@@ -332,7 +332,7 @@
     if (!_isForCurrency ||
         !significantDigitsInUse ||
         _style == null ||
-        _style.isFallback) {
+        _style!.isFallback) {
       return super.minimumFractionDigits;
     } else {
       return 0;
@@ -365,7 +365,7 @@
     // that we pick the right style based on the rounded form and format 999999
     // as 1M rather than 1000K.
     var originalLength = NumberFormat.numberOfIntegerDigits(number);
-    var additionalDigits = originalLength - significantDigits;
+    var additionalDigits = originalLength - significantDigits!;
     var digitLength = originalLength;
     if (additionalDigits > 0) {
       var divisor = pow(10, additionalDigits);
@@ -401,7 +401,7 @@
   }
 
   /// Returns text parsed into a number if possible, else returns null.
-  num _tryParsing(String text) {
+  num? _tryParsing(String text) {
     try {
       return super.parse(text);
     } on FormatException {
diff --git a/lib/src/intl/date_computation.dart b/lib/src/intl/date_computation.dart
index ed5566f..899f1d4 100644
--- a/lib/src/intl/date_computation.dart
+++ b/lib/src/intl/date_computation.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
 /// Given a month and day number, return the day of the year, all one-based.
 ///
 /// For example,
diff --git a/lib/src/intl/micro_money.dart b/lib/src/intl/micro_money.dart
index f773660..1d4ea39 100644
--- a/lib/src/intl/micro_money.dart
+++ b/lib/src/intl/micro_money.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 /// Used primarily for currency formatting, this number-like class stores
 /// millionths of a currency unit, typically as an Int64.
diff --git a/lib/src/intl/number_format.dart b/lib/src/intl/number_format.dart
index d3d8454..811646d 100644
--- a/lib/src/intl/number_format.dart
+++ b/lib/src/intl/number_format.dart
@@ -1,13 +1,12 @@
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 import 'dart:math';
 
-import 'package:intl/intl.dart';
 import 'package:intl/number_symbols.dart';
 import 'package:intl/number_symbols_data.dart';
+import 'package:intl/src/intl_helpers.dart' as helpers;
 
 import 'constants.dart' as constants;
 import 'number_format_parser.dart';
@@ -19,7 +18,7 @@
 
 /// The function that we pass internally to NumberFormat to get
 /// the appropriate pattern (e.g. currency)
-typedef _PatternGetter = String Function(NumberSymbols);
+typedef _PatternGetter = String? Function(NumberSymbols);
 
 /// Provides the ability to format a number in a locale-specific way.
 ///
@@ -91,14 +90,14 @@
   int maximumFractionDigits;
   int minimumFractionDigits;
   int minimumExponentDigits;
-  int _significantDigits;
+  int? _significantDigits;
 
   ///  How many significant digits should we print.
   ///
   ///  Note that if significantDigitsInUse is the default false, this
   ///  will be ignored.
-  int get significantDigits => _significantDigits;
-  set significantDigits(int x) {
+  int? get significantDigits => _significantDigits;
+  set significantDigits(int? x) {
     _significantDigits = x;
     significantDigitsInUse = true;
   }
@@ -114,7 +113,7 @@
 
   /// Stores the pattern used to create this format. This isn't used, but
   /// is helpful in debugging.
-  final String _pattern;
+  final String? _pattern;
 
   /// The locale in which we print numbers.
   final String _locale;
@@ -123,7 +122,7 @@
   final NumberSymbols _symbols;
 
   /// The name of the currency to print, in ISO 4217 form.
-  String currencyName;
+  String? currencyName;
 
   /// The symbol to be used when formatting this as currency.
   ///
@@ -145,7 +144,7 @@
   ///       NumberFormat.currency(locale: 'en_US')
   /// will format with two, which is the default for that locale.
   ///
-  final int decimalDigits;
+  final int? decimalDigits;
 
   /// Transient internal state in which to build up the result of the format
   /// operation. We can have this be just an instance variable because Dart is
@@ -156,25 +155,25 @@
 
   /// Create a number format that prints using [newPattern] as it applies in
   /// [locale].
-  factory NumberFormat([String newPattern, String locale]) =>
+  factory NumberFormat([String? newPattern, String? locale]) =>
       NumberFormat._forPattern(locale, (x) => newPattern);
 
   /// Create a number format that prints as DECIMAL_PATTERN.
-  factory NumberFormat.decimalPattern([String locale]) =>
+  factory NumberFormat.decimalPattern([String? locale]) =>
       NumberFormat._forPattern(locale, (x) => x.DECIMAL_PATTERN);
 
   /// Create a number format that prints as PERCENT_PATTERN.
-  factory NumberFormat.percentPattern([String locale]) =>
+  factory NumberFormat.percentPattern([String? locale]) =>
       NumberFormat._forPattern(locale, (x) => x.PERCENT_PATTERN);
 
   /// Create a number format that prints as PERCENT_PATTERN.
   factory NumberFormat.decimalPercentPattern(
-          {String locale, int decimalDigits}) =>
+          {String? locale, int? decimalDigits}) =>
       NumberFormat._forPattern(locale, (x) => x.PERCENT_PATTERN,
           decimalDigits: decimalDigits);
 
   /// Create a number format that prints as SCIENTIFIC_PATTERN.
-  factory NumberFormat.scientificPattern([String locale]) =>
+  factory NumberFormat.scientificPattern([String? locale]) =>
       NumberFormat._forPattern(locale, (x) => x.SCIENTIFIC_PATTERN);
 
   /// A regular expression to validate currency names are exactly three
@@ -190,7 +189,7 @@
   ///           .currencyPattern(Intl.defaultLocale, "€");
   @Deprecated('Use NumberFormat.currency')
   factory NumberFormat.currencyPattern(
-      [String locale, String currencyNameOrSymbol]) {
+      [String? locale, String? currencyNameOrSymbol]) {
     // If it looks like an iso4217 name, pass as name, otherwise as symbol.
     if (currencyNameOrSymbol != null &&
         _checkCurrencyName.hasMatch(currencyNameOrSymbol)) {
@@ -238,11 +237,11 @@
   /// unsupported formats (e.g. accounting format for currencies.)
   // TODO(alanknight): Should we allow decimalDigits on other numbers.
   factory NumberFormat.currency(
-          {String locale,
-          String name,
-          String symbol,
-          int decimalDigits,
-          String customPattern}) =>
+          {String? locale,
+          String? name,
+          String? symbol,
+          int? decimalDigits,
+          String? customPattern}) =>
       NumberFormat._forPattern(
           locale, (x) => customPattern ?? x.CURRENCY_PATTERN,
           name: name,
@@ -274,7 +273,7 @@
   ///       NumberFormat.simpleCurrency(locale: 'en_US')
   /// will format with two, which is the default for that locale.
   factory NumberFormat.simpleCurrency(
-      {String locale, String name, int decimalDigits}) {
+      {String? locale, String? name, int? decimalDigits}) {
     return NumberFormat._forPattern(locale, (x) => x.CURRENCY_PATTERN,
         name: name,
         decimalDigits: decimalDigits,
@@ -304,14 +303,14 @@
   /// The [currencySymbol] can either be specified directly, or we can pass a
   /// function [computeCurrencySymbol] that will compute it later, given other
   /// information, typically the verified locale.
-  factory NumberFormat._forPattern(String locale, _PatternGetter getPattern,
-      {String name,
-      String currencySymbol,
-      int decimalDigits,
+  factory NumberFormat._forPattern(String? locale, _PatternGetter getPattern,
+      {String? name,
+      String? currencySymbol,
+      int? decimalDigits,
       bool lookupSimpleCurrencySymbol = false,
       bool isForCurrency = false}) {
-    locale = Intl.verifiedLocale(locale, localeExists);
-    var symbols = numberFormatSymbols[locale];
+    locale = helpers.verifiedLocale(locale, localeExists, null);
+    var symbols = numberFormatSymbols[locale] as NumberSymbols;
     var localeZero = symbols.ZERO_DIGIT.codeUnitAt(0);
     var zeroOffset = localeZero - constants.asciiZeroCodeUnit;
     name ??= symbols.DEF_CURRENCY_CODE;
@@ -365,7 +364,7 @@
 
   /// A number format for compact representations, e.g. "1.2M" instead
   /// of "1,200,000".
-  factory NumberFormat.compact({String locale}) {
+  factory NumberFormat.compact({String? locale}) {
     return _CompactNumberFormat(
         locale: locale,
         formatType: _CompactFormatType.COMPACT_DECIMAL_SHORT_PATTERN);
@@ -373,7 +372,7 @@
 
   /// A number format for "long" compact representations, e.g. "1.2 million"
   /// instead of of "1,200,000".
-  factory NumberFormat.compactLong({String locale}) {
+  factory NumberFormat.compactLong({String? locale}) {
     return _CompactNumberFormat(
         locale: locale,
         formatType: _CompactFormatType.COMPACT_DECIMAL_LONG_PATTERN);
@@ -384,7 +383,7 @@
   /// based on the currency name or the locale. See
   /// [NumberFormat.simpleCurrency].
   factory NumberFormat.compactSimpleCurrency(
-      {String locale, String name, int decimalDigits}) {
+      {String? locale, String? name, int? decimalDigits}) {
     return _CompactNumberFormat(
         locale: locale,
         formatType: _CompactFormatType.COMPACT_DECIMAL_SHORT_CURRENCY_PATTERN,
@@ -398,7 +397,7 @@
   /// A number format for compact currency representations, e.g. "$1.2M" instead
   /// of "$1,200,000".
   factory NumberFormat.compactCurrency(
-      {String locale, String name, String symbol, int decimalDigits}) {
+      {String? locale, String? name, String? symbol, int? decimalDigits}) {
     return _CompactNumberFormat(
         locale: locale,
         formatType: _CompactFormatType.COMPACT_DECIMAL_SHORT_CURRENCY_PATTERN,
@@ -439,7 +438,7 @@
 
   /// Parse the number represented by the string. If it's not
   /// parseable, throws a [FormatException].
-  num parse(String text) => NumberParser(this, text).value;
+  num parse(String text) => NumberParser(this, text).value!;
 
   /// Format the main part of the number in the form dictated by the pattern.
   void _formatNumber(number) {
@@ -604,6 +603,7 @@
       /// If we have significant digits, recalculate the number of fraction
       /// digits based on that.
       if (significantDigitsInUse) {
+        var significantDigits = this.significantDigits!;
         var integerLength = numberOfIntegerDigits(integerPart);
         var remainingSignificantDigits =
             significantDigits - _multiplierDigits - integerLength;
@@ -614,7 +614,7 @@
           integerPart = (integerPart / divideBy).round() * divideBy;
         }
       }
-      power = pow(10, fractionDigits);
+      power = pow(10, fractionDigits) as int;
       digitMultiplier = power * multiplier;
 
       // Multiply out to the number of decimal places and the percent, then
@@ -684,9 +684,9 @@
   String _mainIntegerDigits(integer) {
     if (integer == 0) return '';
     var digits = integer.toString();
-    if (significantDigitsInUse && digits.length > significantDigits) {
+    if (significantDigitsInUse && digits.length > significantDigits!) {
       digits = digits.substring(0, significantDigits) +
-          ''.padLeft(digits.length - significantDigits, '0');
+          ''.padLeft(digits.length - significantDigits!, '0');
     }
     // If we have a fixed-length int representation, it can have a negative
     // number whose negation is also negative, e.g. 2^-63 in 64-bit.
diff --git a/lib/src/intl/number_format_parser.dart b/lib/src/intl/number_format_parser.dart
index a2609b1..e4c8871 100644
--- a/lib/src/intl/number_format_parser.dart
+++ b/lib/src/intl/number_format_parser.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 import 'dart:math';
 
 import 'package:intl/number_symbols.dart';
@@ -37,12 +36,11 @@
   bool useSignForPositiveExponent = false;
   bool useExponentialNotation = false;
 
-  int decimalDigits;
+  int? decimalDigits;
 
   // [decimalDigits] is both input and output of parsing.
-  NumberFormatParseResult(NumberSymbols symbols, this.decimalDigits) {
-    negativePrefix = symbols.MINUS_SIGN;
-  }
+  NumberFormatParseResult(NumberSymbols symbols, this.decimalDigits)
+      : negativePrefix = symbols.MINUS_SIGN;
 }
 
 /// Private class that parses the numeric formatting pattern and sets the
@@ -90,7 +88,7 @@
   ///
   /// [decimalDigits] is optional, if specified it overrides the default.
   NumberFormatParser(this.symbols, String input, this.isForCurrency,
-      this.currencySymbol, this.currencyName, int decimalDigits)
+      this.currencySymbol, this.currencyName, int? decimalDigits)
       : result = NumberFormatParseResult(symbols, decimalDigits),
         pattern = StringIterator(input) {
     pattern.moveNext();
@@ -98,11 +96,11 @@
 
   static NumberFormatParseResult parse(
           NumberSymbols symbols,
-          String input,
+          String? input,
           bool isForCurrency,
           String currencySymbol,
           String currencyName,
-          int decimalDigits) =>
+          int? decimalDigits) =>
       input == null
           ? NumberFormatParseResult(symbols, decimalDigits)
           : (NumberFormatParser(symbols, input, isForCurrency, currencySymbol,
@@ -115,7 +113,7 @@
   /// not specified.
   int get _defaultDecimalDigits =>
       currencyFractionDigits[currencyName.toUpperCase()] ??
-      currencyFractionDigits['DEFAULT'];
+      currencyFractionDigits['DEFAULT']!;
 
   /// Parse the input pattern and update [result].
   void _parse() {
@@ -129,7 +127,9 @@
       result.negativePrefix = _parseAffix();
       // Skip over the negative trunk, verifying that it's identical to the
       // positive trunk.
-      for (var each in StringIterable(trunk)) {
+      var trunkIterator = StringIterator(trunk);
+      while (trunkIterator.moveNext()) {
+        var each = trunkIterator.current;
         if (pattern.current != each && pattern.current != null) {
           throw FormatException(
               'Positive and negative trunks must be the same', trunk);
@@ -147,8 +147,8 @@
       result.decimalDigits ??= _defaultDecimalDigits;
     }
     if (result.decimalDigits != null) {
-      result.minimumFractionDigits = result.decimalDigits;
-      result.maximumFractionDigits = result.decimalDigits;
+      result.minimumFractionDigits = result.decimalDigits!;
+      result.maximumFractionDigits = result.decimalDigits!;
     }
   }
 
diff --git a/lib/src/intl/number_parser.dart b/lib/src/intl/number_parser.dart
index 318a18b..cc0e86c 100644
--- a/lib/src/intl/number_parser.dart
+++ b/lib/src/intl/number_parser.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 import 'package:intl/number_symbols.dart';
 
@@ -26,7 +25,7 @@
 
   /// The result of parsing [text] according to [format]. Automatically
   /// populated in the constructor.
-  num value;
+  num? value;
 
   /// The symbols used by our format.
   NumberSymbols get symbols => format.symbols;
@@ -79,7 +78,7 @@
   Map<String, Function> get replacements =>
       _replacements ??= _initializeReplacements();
 
-  Map<String, Function> _replacements;
+  Map<String, Function>? _replacements;
 
   Map<String, Function> _initializeReplacements() => {
         symbols.DECIMAL_SEP: () => '.',
@@ -119,7 +118,7 @@
 
   /// Turn [char] into a number representing a digit, or null if it doesn't
   /// represent a digit in this locale.
-  int asDigit(String char) {
+  int? asDigit(String char) {
     var charCode = char.codeUnitAt(0);
     var digitValue = charCode - _localeZero;
     if (digitValue >= 0 && digitValue < 10) {
@@ -181,7 +180,7 @@
 
     for (var key in replacements.keys) {
       if (input.startsWith(key)) {
-        _normalized.write(replacements[key]());
+        _normalized.write(replacements[key]!());
         input.read(key.length);
         return;
       }
@@ -235,7 +234,7 @@
     }
 
     var normalizedText = _normalized.toString();
-    num parsed = int.tryParse(normalizedText);
+    num? parsed = int.tryParse(normalizedText);
     parsed ??= double.parse(normalizedText);
     return parsed / scale;
   }
diff --git a/lib/src/intl/string_iterator.dart b/lib/src/intl/string_iterator.dart
index 839574d..73c4043 100644
--- a/lib/src/intl/string_iterator.dart
+++ b/lib/src/intl/string_iterator.dart
@@ -1,29 +1,17 @@
 // Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
-// TODO(nweiz): remove this when issue 3780 is fixed.
-import 'dart:collection';
-
-/// Provides an Iterable that wraps [_iterator] so it can be used in a `for`
-/// loop.
-class StringIterable extends IterableBase<String> {
-  final Iterator<String> iterator;
-
-  StringIterable(String s) : iterator = StringIterator(s);
-}
-
-/// Provides an iterator over a string as a list of substrings, and also
+/// Provides an 'iterator' over a string as a list of substrings, and also
 /// gives us a lookahead of one via the [peek] method.
-class StringIterator implements Iterator<String> {
+class StringIterator {
   final String input;
   int nextIndex = 0;
-  String _current;
+  String? _current;
 
-  StringIterator(input) : input = _validate(input);
+  StringIterator(this.input);
 
-  String get current => _current;
+  String? get current => _current;
 
   bool moveNext() {
     if (nextIndex >= input.length) {
@@ -34,12 +22,5 @@
     return true;
   }
 
-  String get peek => nextIndex >= input.length ? null : input[nextIndex];
-
-  Iterator<String> get iterator => this;
-
-  static String _validate(input) {
-    if (input is! String) throw ArgumentError(input);
-    return input;
-  }
+  String? get peek => nextIndex >= input.length ? null : input[nextIndex];
 }
diff --git a/lib/src/intl/text_direction.dart b/lib/src/intl/text_direction.dart
index e9b5649..4fb1163 100644
--- a/lib/src/intl/text_direction.dart
+++ b/lib/src/intl/text_direction.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+// ignore_for_file: constant_identifier_names
+
 /// Represents directionality of text.
 ///
 /// In most cases, it is preferable to use bidi_formatter.dart, which provides
diff --git a/lib/src/intl_helpers.dart b/lib/src/intl_helpers.dart
index 6b3d992..86df1ed 100644
--- a/lib/src/intl_helpers.dart
+++ b/lib/src/intl_helpers.dart
@@ -14,7 +14,7 @@
 
 /// Type for the callback action when a message translation is not found.
 typedef MessageIfAbsent = String Function(
-    String messageText, List<Object> args);
+    String? messageText, List<Object>? args);
 
 /// This is used as a marker for a locale data map that hasn't been initialized,
 /// and will throw an exception on any usage that isn't the fallback
@@ -124,7 +124,7 @@
 /// If a message is a string literal without interpolation, compute
 /// a name based on that and the meaning, if present.
 // NOTE: THIS LOGIC IS DUPLICATED IN intl_translation AND THE TWO MUST MATCH.
-String computeMessageName(String? name, String text, String? meaning) {
+String? computeMessageName(String? name, String? text, String? meaning) {
   if (name != null && name != '') return name;
   return meaning == null ? text : '${text}_$meaning';
 }
diff --git a/lib/src/lazy_locale_data.dart b/lib/src/lazy_locale_data.dart
index caddc95..94b395c 100644
--- a/lib/src/lazy_locale_data.dart
+++ b/lib/src/lazy_locale_data.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 /// This defines a class for loading locale data incrementally from
 /// an external source as JSON. The external sources expected are either
@@ -41,10 +40,9 @@
   /// from the remote data (which typically comes in as a Map). The
   /// [keys] lists the set of remotely available locale names so we know which
   /// things can be fetched without having to check remotely.
-  LazyLocaleData(this._reader, this._creationFunction, this.availableLocales) {
-    map = {};
-    availableLocaleSet = Set.from(availableLocales);
-  }
+  LazyLocaleData(this._reader, this._creationFunction, this.availableLocales)
+      : map = {},
+        availableLocaleSet = Set.from(availableLocales);
 
   ///  Tests if we have data for the locale available. Note that this returns
   /// true even if the data is known to be available remotely but not yet
@@ -81,6 +79,7 @@
   /// initializeDateFormatting instead.
   Future<void> initLocale(String localeName) {
     var data = _reader.read(localeName);
+    // ignore: void_checks
     return jsonData(data).then((input) {
       map[localeName] = _creationFunction(input);
     });
diff --git a/lib/src/plural_rules.dart b/lib/src/plural_rules.dart
index 0ad0493..3c7d3d2 100644
--- a/lib/src/plural_rules.dart
+++ b/lib/src/plural_rules.dart
@@ -1,7 +1,6 @@
 // Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-// @dart=2.9
 
 /// Provides locale-specific plural rules. Based on pluralrules.js from Closure.
 ///
@@ -32,11 +31,11 @@
 
 /// This must be called before evaluating a new rule, because we're using
 /// library-global state to both keep the rules terse and minimize space.
-void startRuleEvaluation(num howMany, [int precision = 0]) {
+void startRuleEvaluation(num howMany, [int? precision = 0]) {
   _n = howMany;
   _precision = precision;
   _i = _n.round();
-  _updateVF(_n, _precision);
+  _updateVF(_n);
   _updateWT(_f, _v);
 }
 
@@ -47,21 +46,21 @@
 // not introduce a subclass per locale or have instance tear-offs which
 // we can't cache. This is fine as long as these methods aren't async, which
 // they should never be.
-num _n;
+num _n = 0;
 
 /// The integer part of [_n]
-int _i;
-int _precision;
+int _i = 0;
+int? _precision;
 
 /// Returns the number of digits in the fractional part of a number
 /// (3.1416 => 4)
 ///
-/// Takes the item count [n] and a [precision].
+/// Takes the item count [n] and uses [_precision].
 /// That's because a just looking at the value of a number is not enough to
 /// decide the plural form. For example "1 dollar" vs "1.00 dollars", the
 /// value is 1, but how it is formatted also matters.
-int _decimals(num n, int precision) {
-  var str = _precision == null ? '$n' : n.toStringAsFixed(precision);
+int _decimals(num n) {
+  var str = _precision == null ? '$n' : n.toStringAsFixed(_precision!);
   var result = str.indexOf('.');
   return (result == -1) ? 0 : str.length - result - 1;
 }
@@ -71,10 +70,10 @@
 /// The short names for parameters / return match the CLDR syntax and UTS #35
 ///     (https://unicode.org/reports/tr35/tr35-numbers.html#Plural_rules_syntax)
 /// Takes the item count [n] and a [precision].
-void _updateVF(num n, int precision) {
+void _updateVF(num n) {
   var defaultDigits = 3;
 
-  _v = precision ?? math.min(_decimals(n, precision), defaultDigits);
+  _v = _precision ?? math.min(_decimals(n), defaultDigits);
 
   var base = math.pow(10, _v) as int;
   _f = (n * base).floor() % base;
diff --git a/test/plural_test.dart b/test/plural_test.dart
index 045455d..3ad91a3 100644
--- a/test/plural_test.dart
+++ b/test/plural_test.dart
@@ -193,8 +193,10 @@
 
   test('Check null howMany', () {
     expect(plural(0, null), '0:Zero');
-    expect(() => plural(null, null), throwsArgumentError);
-    expect(() => plural(null, 'ru'), throwsArgumentError);
+    expect(() => plural(null, null),
+        anyOf(throwsArgumentError, throwsA(isA<AssertionError>())));
+    expect(() => plural(null, 'ru'),
+        anyOf(throwsArgumentError, throwsA(isA<AssertionError>())));
   });
 
   verifyWithPrecision('1 dollar', 'en', 1, 0);