- THRIFT-5712 - Added Dart 3 Compatibility
diff --git a/compiler/cpp/src/thrift/generate/t_dart_generator.cc b/compiler/cpp/src/thrift/generate/t_dart_generator.cc
index 0055043..34f9d82 100644
--- a/compiler/cpp/src/thrift/generate/t_dart_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_dart_generator.cc
@@ -144,7 +144,8 @@
                          t_type* type,
                          t_const_value* value,
                          bool in_static,
-                         bool defval = false);
+                         bool defval = false,
+                         bool inlineOnly = false);
   std::string render_const_value(ostream& out,
                                  std::string name,
                                  t_type* type,
@@ -237,6 +238,7 @@
   std::string dart_thrift_imports();
   std::string type_name(t_type* ttype);
   std::string base_type_name(t_base_type* tbase);
+  std::string type_name_instantiate(t_type* ttype);
   std::string declare_field(t_field* tfield, bool init = false);
   std::string function_signature(t_function* tfunction);
   std::string argument_list(t_struct* tstruct);
@@ -250,6 +252,10 @@
            || ttype->is_string();
   }
 
+  std::string get_type_suffix(t_type* ttype) {
+    return type_can_be_null(ttype) ? "?" : "";
+  }
+
   vector<std::string> split(const string& s, char delim) {
     vector<std::string> elems;
     stringstream ss(s);
@@ -421,7 +427,7 @@
 
   indent(f_pubspec) << "environment:" << '\n';
   indent_up();
-  indent(f_pubspec) << "sdk: '>=1.24.3 <3.0.0'" << '\n';
+  indent(f_pubspec) << "sdk: '>=2.12.0 <4.0.0'" << '\n';
   indent_down();
   f_pubspec << '\n';
 
@@ -569,11 +575,12 @@
                                         t_type* type,
                                         t_const_value* value,
                                         bool in_static,
-                                        bool defval) {
+                                        bool defval,
+                                        bool inlineOnly) {
   type = get_true_type(type);
 
   indent(out);
-  if (!defval) {
+  if (!defval && !inlineOnly) {
     out << (in_static ? "var " : "static final ");
   }
   if (type->is_base_type()) {
@@ -594,7 +601,11 @@
     vector<t_field*>::const_iterator f_iter;
     const map<t_const_value*, t_const_value*, t_const_value::value_compare>& val = value->get_map();
     map<t_const_value*, t_const_value*, t_const_value::value_compare>::const_iterator v_iter;
-    out << type_name(type) << " " << name << " = new " << type_name(type) << "()";
+    if (!inlineOnly) {
+      out << type_name(type) << " " << name << " = new " << type_name(type) << "()";
+    } else {
+      out << "new " << type_name(type) << "()";
+    }
     indent_up();
     for (v_iter = val.begin(); v_iter != val.end(); ++v_iter) {
       t_type* field_type = nullptr;
@@ -611,12 +622,20 @@
       indent(out) << ".." << v_iter->first->get_string() << " = " << val;
     }
     indent_down();
-    out << ";" << '\n';
+    if (!inlineOnly) {
+      out << ";";
+    }
+    out << '\n';
+
   } else if (type->is_map()) {
-    if (!defval) {
+    if (!defval && !inlineOnly) {
       out << type_name(type) << " ";
     }
-    out << name << " =";
+
+    if (!inlineOnly) {
+      out << name<< " =";
+    }
+
     scope_up(out);
 
     t_type* ktype = ((t_map*)type)->get_key_type();
@@ -629,14 +648,17 @@
       string val = render_const_value(out, name, vtype, v_iter->second);
       indent(out) << key << ": " << val << "," << '\n';
     }
-    scope_down(out, string(";") + "\n");
+    scope_down(out, (inlineOnly? string("") : string(";")) + "\n");
 
     out << '\n';
   } else if (type->is_list() || type->is_set()) {
-    if (!defval) {
+    if (!defval && !inlineOnly) {
       out << type_name(type) << " ";
     }
-    out << name << " = ";
+    if (!inlineOnly) {
+      out << name << " =";
+    }
+
     t_type* etype;
     if (type->is_list()) {
       out << "[" << '\n';
@@ -656,9 +678,9 @@
     indent_down();
 
     if (type->is_list()) {
-      indent(out) << "];" << '\n';
+      indent(out) << "]" << (inlineOnly? "" : ";") << '\n';
     } else {
-      indent(out) << "]);" << '\n';
+      indent(out) << "])" << (inlineOnly? "" : ";") << '\n';
     }
 
   } else {
@@ -671,6 +693,7 @@
                                            t_type* type,
                                            t_const_value* value) {
   (void)name;
+  (void)out;
   type = get_true_type(type);
   std::ostringstream render;
 
@@ -703,9 +726,7 @@
     render << value->get_integer();
   } else {
     string t = tmp("tmp");
-    print_const_value(out, t, type, value, true);
-    out << '\n';
-    render << t;
+    print_const_value(render, t, type, value, true, false, true);
   }
 
   return render.str();
@@ -799,7 +820,7 @@
 
   for (m_iter = members.begin(); m_iter != members.end(); ++m_iter) {
     generate_dart_doc(out, *m_iter);
-    indent(out) << type_name((*m_iter)->get_type()) + " _"
+    indent(out) << type_name((*m_iter)->get_type()) + get_type_suffix((*m_iter)->get_type())  + " _"
                 << get_member_name((*m_iter)->get_name()) << init_value(*m_iter) << ";" << '\n';
 
     indent(out) << "static const int " << upcase_string((*m_iter)->get_name())
@@ -1129,7 +1150,7 @@
                                                       t_struct* tstruct) {
 
   // create the setter
-  indent(out) << "setFieldValue(int fieldID, Object value)";
+  indent(out) << "setFieldValue(int fieldID, Object? value)";
   scope_up(out);
 
   indent(out) << "switch (fieldID)";
@@ -1140,6 +1161,7 @@
   vector<t_field*>::const_iterator f_iter;
   for (f_iter = fields.begin(); f_iter != fields.end(); ++f_iter) {
     t_field* field = *f_iter;
+    t_type* type = get_true_type(field->get_type());
     std::string field_name = get_member_name(field->get_name());
 
     indent(out) << "case " << upcase_string(field_name) << ":" << '\n';
@@ -1151,7 +1173,8 @@
 
     scope_down(out, " else");
     scope_up(out);
-    indent(out) << "this." << field_name << " = value;" << '\n';
+
+    indent(out) << "this." << field_name << " = value as " << type_name(type) << ";" << '\n';
     scope_down(out);
 
     indent(out) << "break;" << '\n';
@@ -1220,11 +1243,11 @@
 
     // Simple getter
     generate_dart_doc(out, field);
-    indent(out) << type_name(type) << " get " << field_name << " => this._" << field_name << ";" << '\n' << '\n';
+    indent(out) << type_name(type) << get_type_suffix(type) << " get " << field_name << " => this._" << field_name << ";" << '\n' << '\n';
 
     // Simple setter
     generate_dart_doc(out, field);
-    indent(out) << "set " << field_name << "(" << type_name(type) << " " << field_name << ")";
+    indent(out) << "set " << field_name << "(" << type_name(type) << get_type_suffix(type)  << " " << field_name << ")";
     scope_up(out);
     indent(out) << "this._" << field_name << " = " << field_name << ";" << '\n';
     generate_isset_set(out, field);
@@ -1293,7 +1316,7 @@
     if (field->get_type()->is_binary()) {
       indent(out) << "ret.write(\"BINARY\");" << '\n';
     } else if (field->get_type()->is_enum()) {
-      indent(out) << "String " << field_name << "_name = "
+      indent(out) << "String? " << field_name << "_name = "
                   << get_ttype_class_name(field->get_type())
                   << ".VALUES_TO_NAMES[this." << field_name << "];" << '\n';
       indent(out) << "if (" << field_name << "_name != null)";
@@ -1464,7 +1487,7 @@
   scope_up(f_service_);
   f_service_ << '\n';
 
-  indent(f_service_) << class_name << "(TProtocol iprot, [TProtocol oprot = null])";
+  indent(f_service_) << class_name << "(TProtocol iprot, [TProtocol? oprot = null])";
 
   if (!extends.empty()) {
     indent_up();
@@ -1480,9 +1503,9 @@
   f_service_ << '\n';
 
   if (extends.empty()) {
-    indent(f_service_) << "TProtocol _iprot;" << '\n' << '\n';
+    indent(f_service_) << "late TProtocol _iprot;" << '\n' << '\n';
     indent(f_service_) << "TProtocol get iprot => _iprot;" << '\n' << '\n';
-    indent(f_service_) << "TProtocol _oprot;" << '\n' << '\n';
+    indent(f_service_) << "late TProtocol _oprot;" << '\n' << '\n';
     indent(f_service_) << "TProtocol get oprot => _oprot;" << '\n' << '\n';
     indent(f_service_) << "int _seqid = 0;" << '\n' << '\n';
     indent(f_service_) << "int get seqid => _seqid;" << '\n' << '\n';
@@ -1550,7 +1573,7 @@
         string result_field_name = get_member_name((*x_iter)->get_name());
         indent(f_service_) << "if (result." << result_field_name << " != null)";
         scope_up(f_service_);
-        indent(f_service_) << "throw result." << result_field_name << ";" << '\n';
+        indent(f_service_) << "throw result." << result_field_name << "!;" << '\n';
         scope_down(f_service_);
       }
 
@@ -1585,9 +1608,11 @@
   // Extends stuff
   string extends = "";
   string extends_processor = "";
+  string extends_covariant = "";
   if (tservice->get_extends() != nullptr) {
     extends = get_ttype_class_name(tservice->get_extends());
     extends_processor = " extends " + extends + "Processor";
+    extends_covariant = "covariant ";
   }
 
   // Generate the header portion
@@ -1615,7 +1640,8 @@
   }
   scope_down(f_service_, "\n\n");
 
-  indent(f_service_) << service_name_ << " iface_;" << '\n';
+
+  indent(f_service_) << extends_covariant << "late " << service_name_ << " iface_;" << '\n';
 
   if (extends.empty()) {
     indent(f_service_) << "final Map<String, ProcessFunction> PROCESS_MAP = {};" << '\n';
@@ -1627,7 +1653,7 @@
   indent(f_service_) << "bool process(TProtocol iprot, TProtocol oprot)";
   scope_up(f_service_);
   indent(f_service_) << "TMessage msg = iprot.readMessageBegin();" << '\n';
-  indent(f_service_) << "ProcessFunction fn = PROCESS_MAP[msg.name];" << '\n';
+  indent(f_service_) << "ProcessFunction? fn = PROCESS_MAP[msg.name];" << '\n';
   indent(f_service_) << "if (fn == null)";
   scope_up(f_service_);
   indent(f_service_) << "TProtocolUtil.skip(iprot, TType.STRUCT);" << '\n';
@@ -1850,7 +1876,7 @@
  */
 void t_dart_generator::generate_deserialize_struct(ostream& out, t_struct* tstruct, string prefix) {
   indent(out) << prefix << " = new " << type_name(tstruct) << "();" << '\n';
-  indent(out) << prefix << ".read(iprot);" << '\n';
+  indent(out) << prefix << "!.read(iprot);" << '\n';
 }
 
 /**
@@ -1879,7 +1905,7 @@
     indent(out) << "TList " << obj << " = iprot.readListBegin();" << '\n';
   }
 
-  indent(out) << prefix << " = new " << type_name(ttype) << "();" << '\n';
+  indent(out) << prefix << " = " << type_name_instantiate(ttype) << ";" << '\n';
 
   // For loop iterates over elements
   string i = tmp("_i");
@@ -1925,7 +1951,7 @@
   generate_deserialize_field(out, &fkey);
   generate_deserialize_field(out, &fval);
 
-  indent(out) << prefix << "[" << key << "] = " << val << ";" << '\n';
+  indent(out) << prefix << "![" << key << "] = " << val << ";" << '\n';
 }
 
 /**
@@ -1939,7 +1965,7 @@
 
   generate_deserialize_field(out, &felem);
 
-  indent(out) << prefix << ".add(" << elem << ");" << '\n';
+  indent(out) << prefix << "!.add(" << elem << ");" << '\n';
 }
 
 /**
@@ -1955,7 +1981,7 @@
 
   generate_deserialize_field(out, &felem);
 
-  indent(out) << prefix << ".add(" << elem << ");" << '\n';
+  indent(out) << prefix << "!.add(" << elem << ");" << '\n';
 }
 
 /**
@@ -1967,6 +1993,7 @@
 void t_dart_generator::generate_serialize_field(ostream& out, t_field* tfield, string prefix) {
   t_type* type = get_true_type(tfield->get_type());
   string field_name = get_member_name(tfield->get_name());
+  bool null_allowed = type_can_be_null(type);
 
   // Do nothing for void types
   if (type->is_void()) {
@@ -1990,28 +2017,28 @@
         break;
       case t_base_type::TYPE_STRING:
         if (type->is_binary()) {
-          out << "writeBinary(" << name << ");";
+          out << "writeBinary(" << name << (null_allowed ? "!);" : ");");
         } else {
-          out << "writeString(" << name << ");";
+          out << "writeString(" << name << (null_allowed ? "!);" : ");");
         }
         break;
       case t_base_type::TYPE_BOOL:
-        out << "writeBool(" << name << ");";
+        out << "writeBool(" << name << "!);";
         break;
       case t_base_type::TYPE_I8:
-        out << "writeByte(" << name << ");";
+        out << "writeByte(" << name << "!);";
         break;
       case t_base_type::TYPE_I16:
-        out << "writeI16(" << name << ");";
+        out << "writeI16(" << name << "!);";
         break;
       case t_base_type::TYPE_I32:
-        out << "writeI32(" << name << ");";
+        out << "writeI32(" << name << "!);";
         break;
       case t_base_type::TYPE_I64:
-        out << "writeI64(" << name << ");";
+        out << "writeI64(" << name << "!);";
         break;
       case t_base_type::TYPE_DOUBLE:
-        out << "writeDouble(" << name << ");";
+        out << "writeDouble(" << name << "!);";
         break;
       default:
         throw "compiler error: no Dart name for base type " + t_base_type::t_base_name(tbase);
@@ -2036,7 +2063,7 @@
  */
 void t_dart_generator::generate_serialize_struct(ostream& out, t_struct* tstruct, string prefix) {
   (void)tstruct;
-  indent(out) << prefix << ".write(oprot);" << '\n';
+  indent(out) << prefix << "?.write(oprot);" << '\n';
 }
 
 /**
@@ -2052,22 +2079,22 @@
   if (ttype->is_map()) {
     string iter = tmp("_key");
     indent(out) << "oprot.writeMapBegin(new TMap(" << type_to_enum(((t_map*)ttype)->get_key_type())
-                << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << prefix << ".length));"
+                << ", " << type_to_enum(((t_map*)ttype)->get_val_type()) << ", " << prefix << "!.length));"
                 << '\n';
   } else if (ttype->is_set()) {
     indent(out) << "oprot.writeSetBegin(new TSet(" << type_to_enum(((t_set*)ttype)->get_elem_type())
-                << ", " << prefix << ".length));" << '\n';
+                << ", " << prefix << "!.length));" << '\n';
   } else if (ttype->is_list()) {
     indent(out) << "oprot.writeListBegin(new TList("
-                << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << ".length));"
+                << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", " << prefix << "!.length));"
                 << '\n';
   }
 
   string iter = tmp("elem");
   if (ttype->is_map()) {
-    indent(out) << "for (var " << iter << " in " << prefix << ".keys)";
+    indent(out) << "for (var " << iter << " in " << prefix << "!.keys)";
   } else if (ttype->is_set() || ttype->is_list()) {
-    indent(out) << "for (var " << iter << " in " << prefix << ")";
+    indent(out) << "for (var " << iter << " in " << prefix << "!)";
   }
 
   scope_up(out);
@@ -2102,7 +2129,7 @@
                                                      string map) {
   t_field kfield(tmap->get_key_type(), iter);
   generate_serialize_field(out, &kfield, "");
-  t_field vfield(tmap->get_val_type(), map + "[" + iter + "]");
+  t_field vfield(tmap->get_val_type(), map + "![" + iter + "]");
   generate_serialize_field(out, &vfield, "");
 }
 
@@ -2151,6 +2178,34 @@
 }
 
 /**
+ * Returns instantiate code for a Dart type name
+ *
+ * @param ttype The type
+ * @return Dart type name, i.e. Map<Key, Value>
+ */
+string t_dart_generator::type_name_instantiate(t_type* ttype) {
+  ttype = get_true_type(ttype);
+
+  if (ttype->is_base_type()) {
+    return base_type_name((t_base_type*)ttype);
+  } else if (ttype->is_enum()) {
+    return "int";
+  } else if (ttype->is_map()) {
+    t_map* tmap = (t_map*)ttype;
+    return "<" + type_name(tmap->get_key_type()) + ", "
+                  + type_name(tmap->get_val_type()) + ">{}";
+  } else if (ttype->is_set()) {
+    t_set* tset = (t_set*)ttype;
+    return "<" + type_name(tset->get_elem_type()) + ">{}";
+  } else if (ttype->is_list()) {
+    t_list* tlist = (t_list*)ttype;
+    return "<" + type_name(tlist->get_elem_type()) + ">[]";
+  }
+
+  return get_ttype_class_name(ttype);
+}
+
+/**
  * Returns the Dart type that corresponds to the thrift type.
  *
  * @param tbase The base type
@@ -2241,9 +2296,9 @@
 
   std::string returntype;
   if (tfunction->get_returntype()->is_void()) {
-    returntype = "Future";
+    returntype = "Future<void>";
   } else {
-    returntype = "Future<" + type_name(tfunction->get_returntype()) + ">";
+    returntype = "Future<" + type_name(tfunction->get_returntype()) + get_type_suffix(tfunction->get_returntype()) + ">";
   }
 
   std::string result = returntype + " " + get_member_name(tfunction->get_name()) +
@@ -2267,7 +2322,7 @@
       result += ", ";
     }
     string field_name = get_member_name((*f_iter)->get_name());
-    result += type_name((*f_iter)->get_type()) + " " + field_name;
+    result += type_name((*f_iter)->get_type()) + (type_can_be_null((*f_iter)->get_type()) ? "?" : "") + " " + field_name;
   }
   return result;
 }
@@ -2316,13 +2371,14 @@
 }
 
 std::string t_dart_generator::init_value(t_field* field) {
-  // Do not initialize optional fields
-  if (field->get_req() == t_field::T_OPTIONAL) {
-    return "";
-  }
 
   t_type* ttype = field->get_type();
 
+  if (ttype->is_enum())
+  {
+    return " = 0";
+  }
+
   // Get the actual type for a typedef
   if (ttype->is_typedef()) {
     ttype = ((t_typedef*)ttype)->get_type();
diff --git a/lib/dart/Makefile.am b/lib/dart/Makefile.am
index 74f1404..3518a87 100644
--- a/lib/dart/Makefile.am
+++ b/lib/dart/Makefile.am
@@ -17,6 +17,9 @@
 # under the License.
 #
 
+# Dart build command
+DARTPUB = dart pub
+
 all-local:
 	$(DARTPUB) get
 
diff --git a/lib/dart/lib/src/browser/t_web_socket.dart b/lib/dart/lib/src/browser/t_web_socket.dart
index 1d0bfeb..1667831 100644
--- a/lib/dart/lib/src/browser/t_web_socket.dart
+++ b/lib/dart/lib/src/browser/t_web_socket.dart
@@ -49,19 +49,19 @@
       : _onStateController = StreamController.broadcast(),
         _onErrorController = StreamController.broadcast(),
         _onMessageController = StreamController.broadcast() {
-    if (url == null || !url.hasAuthority || !url.hasPort) {
+    if (!url.hasAuthority || !url.hasPort) {
       throw ArgumentError('Invalid url');
     }
   }
 
-  WebSocket _socket;
+  WebSocket? _socket;
 
   @override
-  bool get isOpen => _socket != null && _socket.readyState == WebSocket.OPEN;
+  bool get isOpen => _socket != null && _socket!.readyState == WebSocket.OPEN;
 
   @override
   bool get isClosed =>
-      _socket == null || _socket.readyState == WebSocket.CLOSED;
+      _socket == null ||_socket!.readyState == WebSocket.CLOSED;
 
   @override
   Future open() {
@@ -71,19 +71,19 @@
     }
 
     _socket = WebSocket(url.toString());
-    _socket.onError.listen(_onError);
-    _socket.onOpen.listen(_onOpen);
-    _socket.onClose.listen(_onClose);
-    _socket.onMessage.listen(_onMessage);
+    _socket!.onError.listen(_onError);
+    _socket!.onOpen.listen(_onOpen);
+    _socket!.onClose.listen(_onClose);
+    _socket!.onMessage.listen(_onMessage);
 
-    return _socket.onOpen.first;
+    return _socket!.onOpen.first;
   }
 
   @override
   Future close() {
     if (_socket != null) {
-      _socket.close();
-      return _socket.onClose.first;
+      _socket!.close();
+      return _socket!.onClose.first;
     } else {
       return Future.value();
     }
@@ -98,7 +98,7 @@
   void _sendRequests() {
     while (isOpen && _requests.isNotEmpty) {
       Uint8List data = _requests.removeAt(0);
-      _socket.sendString(base64.encode(data));
+      _socket!.sendString(base64.encode(data));
     }
   }
 
diff --git a/lib/dart/lib/src/console/t_tcp_socket.dart b/lib/dart/lib/src/console/t_tcp_socket.dart
index 610d633..7c17f42 100644
--- a/lib/dart/lib/src/console/t_tcp_socket.dart
+++ b/lib/dart/lib/src/console/t_tcp_socket.dart
@@ -37,7 +37,7 @@
   @override
   Stream<Uint8List> get onMessage => _onMessageController.stream;
 
-  TTcpSocket(Socket socket)
+  TTcpSocket(Socket? socket)
       : _onStateController = StreamController.broadcast(),
         _onErrorController = StreamController.broadcast(),
         _onMessageController = StreamController.broadcast() {
@@ -46,10 +46,10 @@
     }
 
     _socket = socket;
-    _socket.listen(_onMessage, onError: _onError, onDone: close);
+    _socket!.listen(_onMessage, onError: _onError, onDone: close);
   }
 
-  Socket _socket;
+  Socket? _socket;
 
   @override
   bool get isOpen => _socket != null;
@@ -65,7 +65,7 @@
   @override
   Future close() async {
     if (_socket != null) {
-      await _socket.close();
+      await _socket!.close();
       _socket = null;
     }
 
@@ -74,7 +74,9 @@
 
   @override
   void send(Uint8List data) {
-    _socket.add(data);
+     if (_socket != null) {
+      _socket!.add(data);
+     }
   }
 
   void _onMessage(List<int> message) {
diff --git a/lib/dart/lib/src/console/t_web_socket.dart b/lib/dart/lib/src/console/t_web_socket.dart
index d69cafe..98fdf70 100644
--- a/lib/dart/lib/src/console/t_web_socket.dart
+++ b/lib/dart/lib/src/console/t_web_socket.dart
@@ -38,7 +38,7 @@
   @override
   Stream<Uint8List> get onMessage => _onMessageController.stream;
 
-  TWebSocket(WebSocket socket)
+  TWebSocket(WebSocket? socket)
       : _onStateController = StreamController.broadcast(),
         _onErrorController = StreamController.broadcast(),
         _onMessageController = StreamController.broadcast() {
@@ -47,10 +47,12 @@
     }
 
     _socket = socket;
-    _socket.listen(_onMessage, onError: _onError, onDone: close);
+    _socket!.listen((dynamic message) => _onMessage(message as String),
+      onError: (dynamic error) => _onError(error),
+      onDone: () => close(),);
   }
 
-  WebSocket _socket;
+  WebSocket? _socket;
 
   @override
   bool get isOpen => _socket != null;
@@ -66,7 +68,7 @@
   @override
   Future close() async {
     if (_socket != null) {
-      await _socket.close();
+      await _socket!.close();
       _socket = null;
     }
 
@@ -75,7 +77,9 @@
 
   @override
   void send(Uint8List data) {
-    _socket.add(base64.encode(data));
+    if (_socket != null) {
+      _socket!.add(base64.encode(data));
+    }
   }
 
   void _onMessage(String message) {
diff --git a/lib/dart/lib/src/protocol/t_binary_protocol.dart b/lib/dart/lib/src/protocol/t_binary_protocol.dart
index 9f8f3bf..5ddab2c 100644
--- a/lib/dart/lib/src/protocol/t_binary_protocol.dart
+++ b/lib/dart/lib/src/protocol/t_binary_protocol.dart
@@ -114,7 +114,6 @@
 
   @override
   void writeBool(bool b) {
-    if (b == null) b = false;
     writeByte(b ? 1 : 0);
   }
 
@@ -122,7 +121,6 @@
 
   @override
   void writeByte(int byte) {
-    if (byte == null) byte = 0;
     _byteOut.setUint8(0, byte);
     transport.write(_byteOut.buffer.asUint8List(), 0, 1);
   }
@@ -131,7 +129,6 @@
 
   @override
   void writeI16(int i16) {
-    if (i16 == null) i16 = 0;
     _i16Out.setInt16(0, i16);
     transport.write(_i16Out.buffer.asUint8List(), 0, 2);
   }
@@ -140,7 +137,6 @@
 
   @override
   void writeI32(int i32) {
-    if (i32 == null) i32 = 0;
     _i32Out.setInt32(0, i32);
     transport.write(_i32Out.buffer.asUint8List(), 0, 4);
   }
@@ -149,7 +145,6 @@
 
   @override
   void writeI64(int i64) {
-    if (i64 == null) i64 = 0;
     var i = Int64(i64);
     var bts = i.toBytes();
     for (var j = 0; j < 8; j++) {
@@ -160,7 +155,7 @@
 
   @override
   void writeString(String s) {
-    var bytes = s != null ? _utf8Codec.encode(s) : Uint8List.fromList([]);
+    var bytes = _utf8Codec.encode(s);
     writeI32(bytes.length);
     transport.write(bytes, 0, bytes.length);
   }
@@ -169,7 +164,6 @@
 
   @override
   void writeDouble(double d) {
-    if (d == null) d = 0.0;
     _doubleOut.setFloat64(0, d);
     transport.write(_doubleOut.buffer.asUint8List(), 0, 8);
   }
diff --git a/lib/dart/lib/src/protocol/t_compact_protocol.dart b/lib/dart/lib/src/protocol/t_compact_protocol.dart
index dc26722..341b659 100644
--- a/lib/dart/lib/src/protocol/t_compact_protocol.dart
+++ b/lib/dart/lib/src/protocol/t_compact_protocol.dart
@@ -54,7 +54,8 @@
   static const int TYPE_MAP = 0x0B;
   static const int TYPE_STRUCT = 0x0C;
 
-  static final List<int> _typeMap = List.unmodifiable(List(16)
+static final List<int> _typeMap = List<int>.unmodifiable(
+  List<int>.filled(16, 0)
     ..[TType.STOP] = TType.STOP
     ..[TType.BOOL] = TYPE_BOOLEAN_TRUE
     ..[TType.BYTE] = TYPE_BYTE
@@ -66,7 +67,8 @@
     ..[TType.LIST] = TYPE_LIST
     ..[TType.SET] = TYPE_SET
     ..[TType.MAP] = TYPE_MAP
-    ..[TType.STRUCT] = TYPE_STRUCT);
+    ..[TType.STRUCT] = TYPE_STRUCT,
+);
 
   static const Utf8Codec _utf8Codec = Utf8Codec();
 
@@ -74,8 +76,8 @@
   DoubleLinkedQueue<int> _lastField = DoubleLinkedQueue<int>();
   int _lastFieldId = 0;
 
-  TField _booleanField;
-  bool _boolValue;
+  TField? _booleanField;
+  bool? _boolValue;
 
   final Uint8List tempList = Uint8List(10);
   final ByteData tempBD = ByteData(10);
@@ -169,10 +171,9 @@
 
   @override
   void writeBool(bool b) {
-    if (b == null) b = false;
     if (_booleanField != null) {
       _writeFieldBegin(
-          _booleanField, b ? TYPE_BOOLEAN_TRUE : TYPE_BOOLEAN_FALSE);
+          _booleanField!, b ? TYPE_BOOLEAN_TRUE : TYPE_BOOLEAN_FALSE);
       _booleanField = null;
     } else {
       writeByte(b ? TYPE_BOOLEAN_TRUE : TYPE_BOOLEAN_FALSE);
@@ -181,40 +182,34 @@
 
   @override
   void writeByte(int b) {
-    if (b == null) b = 0;
     tempList[0] = b;
     transport.write(tempList, 0, 1);
   }
 
   @override
   void writeI16(int i16) {
-    if (i16 == null) i16 = 0;
     _writeVarInt32(_int32ToZigZag(Int32(i16)));
   }
 
   @override
   void writeI32(int i32) {
-    if (i32 == null) i32 = 0;
     _writeVarInt32(_int32ToZigZag(Int32(i32)));
   }
 
   @override
   void writeI64(int i64) {
-    if (i64 == null) i64 = 0;
     _writeVarInt64(_int64ToZigZag(Int64(i64)));
   }
 
   @override
   void writeDouble(double d) {
-    if (d == null) d = 0.0;
     tempBD.setFloat64(0, d, Endian.little);
     transport.write(tempBD.buffer.asUint8List(), 0, 8);
   }
 
   @override
   void writeString(String str) {
-    Uint8List bytes =
-        str != null ? _utf8Codec.encode(str) : Uint8List.fromList([]);
+    Uint8List bytes = _utf8Codec.encode(str);
     writeBinary(bytes);
   }
 
@@ -374,7 +369,7 @@
   @override
   bool readBool() {
     if (_boolValue != null) {
-      bool result = _boolValue;
+      bool result = _boolValue!;
       _boolValue = null;
       return result;
     }
diff --git a/lib/dart/lib/src/protocol/t_json_protocol.dart b/lib/dart/lib/src/protocol/t_json_protocol.dart
index 4aaf20c..9752a41 100644
--- a/lib/dart/lib/src/protocol/t_json_protocol.dart
+++ b/lib/dart/lib/src/protocol/t_json_protocol.dart
@@ -32,15 +32,16 @@
 
   static const Utf8Codec utf8Codec = Utf8Codec();
 
-  _BaseContext _context;
-  _BaseContext _rootContext;
-  _LookaheadReader _reader;
+  late _BaseContext _context;
+  late _BaseContext _rootContext;
+  late _LookaheadReader _reader;
 
   final List<_BaseContext> _contextStack = [];
   final Uint8List _tempBuffer = Uint8List(4);
 
   TJsonProtocol(TTransport transport) : super(transport) {
     _rootContext = _BaseContext(this);
+    _context = _rootContext;
     _reader = _LookaheadReader(this);
     _resetContext();
   }
@@ -122,8 +123,6 @@
   }
 
   void _writeJsonInteger(int i) {
-    if (i == null) i = 0;
-
     _context.write();
     String str = i.toString();
 
@@ -137,8 +136,6 @@
   }
 
   void _writeJsonDouble(double d) {
-    if (d == null) d = 0.0;
-
     _context.write();
     String str = d.toString();
     bool escapeNumbers = d.isNaN || d.isInfinite || _context.escapeNumbers;
@@ -267,7 +264,6 @@
 
   @override
   void writeBool(bool b) {
-    if (b == null) b = false;
     _writeJsonInteger(b ? 1 : 0);
   }
 
@@ -298,7 +294,7 @@
 
   @override
   void writeString(String s) {
-    var bytes = s != null ? utf8Codec.encode(s) : Uint8List.fromList([]);
+    var bytes = utf8Codec.encode(s);
     _writeJsonString(bytes);
   }
 
@@ -418,7 +414,7 @@
       Uint8List bytes = _readJsonString(skipContext: true);
       double d;
       try {
-        d = double.tryParse(utf8Codec.decode(bytes));
+        d = double.parse(utf8Codec.decode(bytes));
       } catch (_) {
         throw TProtocolError(TProtocolErrorType.INVALID_DATA,
             "Bad data encounted in numeric data");
@@ -675,7 +671,7 @@
           TProtocolErrorType.NOT_IMPLEMENTED, "Unrecognized type");
     }
 
-    return _TYPE_ID_TO_NAME_BYTES[typeId];
+    return _TYPE_ID_TO_NAME_BYTES[typeId]!;
   }
 
   static final Map<String, int> _NAME_TO_TYPE_ID = Map.unmodifiable({
@@ -699,7 +695,7 @@
           TProtocolErrorType.NOT_IMPLEMENTED, "Unrecognized type");
     }
 
-    return _NAME_TO_TYPE_ID[name];
+    return _NAME_TO_TYPE_ID[name]!;
   }
 
   static final Set<int> _JSON_NUMERICS = Set.from([
diff --git a/lib/dart/lib/src/protocol/t_multiplexed_protocol.dart b/lib/dart/lib/src/protocol/t_multiplexed_protocol.dart
index 693e58b..42030a8 100644
--- a/lib/dart/lib/src/protocol/t_multiplexed_protocol.dart
+++ b/lib/dart/lib/src/protocol/t_multiplexed_protocol.dart
@@ -25,11 +25,7 @@
 
   TMultiplexedProtocol(TProtocol protocol, String serviceName)
       : _serviceName = serviceName,
-        super(protocol) {
-    if (serviceName == null) {
-      throw ArgumentError.notNull("serviceName");
-    }
-  }
+        super(protocol);
 
   @override
   void writeMessageBegin(TMessage message) {
diff --git a/lib/dart/lib/src/serializer/t_deserializer.dart b/lib/dart/lib/src/serializer/t_deserializer.dart
index c01ab6b..f284ee6 100644
--- a/lib/dart/lib/src/serializer/t_deserializer.dart
+++ b/lib/dart/lib/src/serializer/t_deserializer.dart
@@ -19,10 +19,10 @@
 
 class TDeserializer {
   final message = TMessage('Deserializer', TMessageType.ONEWAY, 1);
-  TBufferedTransport transport;
-  TProtocol protocol;
+  late TBufferedTransport transport;
+  late TProtocol protocol;
 
-  TDeserializer({TProtocolFactory protocolFactory}) {
+  TDeserializer({TProtocolFactory? protocolFactory}) {
     this.transport = TBufferedTransport();
 
     if (protocolFactory == null) {
diff --git a/lib/dart/lib/src/serializer/t_serializer.dart b/lib/dart/lib/src/serializer/t_serializer.dart
index fb89789..17cab53 100644
--- a/lib/dart/lib/src/serializer/t_serializer.dart
+++ b/lib/dart/lib/src/serializer/t_serializer.dart
@@ -19,10 +19,10 @@
 
 class TSerializer {
   final message = TMessage('Serializer', TMessageType.ONEWAY, 1);
-  TBufferedTransport transport;
-  TProtocol protocol;
+  late TBufferedTransport transport;
+  late TProtocol protocol;
 
-  TSerializer({TProtocolFactory protocolFactory}) {
+  TSerializer({TProtocolFactory? protocolFactory}) {
     this.transport = TBufferedTransport();
 
     if (protocolFactory == null) {
diff --git a/lib/dart/lib/src/t_application_error.dart b/lib/dart/lib/src/t_application_error.dart
index 38449a9..69c0138 100644
--- a/lib/dart/lib/src/t_application_error.dart
+++ b/lib/dart/lib/src/t_application_error.dart
@@ -45,7 +45,7 @@
   static TApplicationError read(TProtocol iprot) {
     TField field;
 
-    String message;
+    String message = "";
     int type = TApplicationErrorType.UNKNOWN;
 
     iprot.readStructBegin();
@@ -87,7 +87,7 @@
   write(TProtocol oprot) {
     oprot.writeStructBegin(_struct);
 
-    if (message != null && message.isNotEmpty) {
+    if (message.isNotEmpty) {
       oprot.writeFieldBegin(_messageField);
       oprot.writeString(message);
       oprot.writeFieldEnd();
diff --git a/lib/dart/lib/src/transport/t_buffered_transport.dart b/lib/dart/lib/src/transport/t_buffered_transport.dart
index f17b2ee..f6c5ea6 100644
--- a/lib/dart/lib/src/transport/t_buffered_transport.dart
+++ b/lib/dart/lib/src/transport/t_buffered_transport.dart
@@ -20,7 +20,7 @@
 /// Buffered implementation of [TTransport].
 class TBufferedTransport extends TTransport {
   final List<int> _writeBuffer = [];
-  Iterator<int> _readIterator;
+  Iterator<int>? _readIterator;
 
   Uint8List consumeWriteBuffer() {
     Uint8List buffer = Uint8List.fromList(_writeBuffer);
@@ -29,7 +29,7 @@
   }
 
   void _setReadBuffer(Uint8List readBuffer) {
-    _readIterator = readBuffer != null ? readBuffer.iterator : null;
+    _readIterator = readBuffer.iterator;
   }
 
   void _reset({bool isOpen = false}) {
@@ -40,7 +40,7 @@
 
   bool get hasReadData => _readIterator != null;
 
-  bool _isOpen;
+  bool _isOpen = false;
   @override
   bool get isOpen => _isOpen;
 
@@ -56,10 +56,6 @@
 
   @override
   int read(Uint8List buffer, int offset, int length) {
-    if (buffer == null) {
-      throw ArgumentError.notNull("buffer");
-    }
-
     if (offset + length > buffer.length) {
       throw ArgumentError("The range exceeds the buffer length");
     }
@@ -69,13 +65,17 @@
     }
 
     int i = 0;
-    while (i < length && _readIterator.moveNext()) {
-      buffer[offset + i] = _readIterator.current;
+    bool hasData = true;
+    while (i < length && hasData) {
+      hasData = _readIterator!.moveNext();
+      if (hasData) {
+      buffer[offset + i] = _readIterator!.current;
       i++;
+      }
     }
 
     // cleanup iterator when we've reached the end
-    if (_readIterator.current == null) {
+    if (!hasData) {
       _readIterator = null;
     }
 
@@ -84,10 +84,6 @@
 
   @override
   void write(Uint8List buffer, int offset, int length) {
-    if (buffer == null) {
-      throw ArgumentError.notNull("buffer");
-    }
-
     if (offset + length > buffer.length) {
       throw ArgumentError("The range exceeds the buffer length");
     }
diff --git a/lib/dart/lib/src/transport/t_framed_transport.dart b/lib/dart/lib/src/transport/t_framed_transport.dart
index 3fc55fa..de09e51 100644
--- a/lib/dart/lib/src/transport/t_framed_transport.dart
+++ b/lib/dart/lib/src/transport/t_framed_transport.dart
@@ -29,16 +29,12 @@
   int _receivedHeaderBytes = 0;
 
   int _bodySize = 0;
-  Uint8List _body;
+  Uint8List? _body;
   int _receivedBodyBytes = 0;
 
-  Completer<Uint8List> _frameCompleter;
+  Completer<Uint8List>? _frameCompleter;
 
-  TFramedTransport(TTransport transport) : _transport = transport {
-    if (transport == null) {
-      throw ArgumentError.notNull("transport");
-    }
-  }
+  TFramedTransport(TTransport transport) : _transport = transport;
 
   @override
   bool get isOpen => _transport.isOpen;
@@ -115,7 +111,7 @@
   void _readFrameBody() {
     var remainingBodyBytes = _bodySize - _receivedBodyBytes;
 
-    int got = _transport.read(_body, _receivedBodyBytes, remainingBodyBytes);
+    int got = _transport.read(_body!, _receivedBodyBytes, remainingBodyBytes);
     if (got < 0) {
       throw TTransportError(
           TTransportErrorType.UNKNOWN, "Socket closed during frame body read");
@@ -124,7 +120,7 @@
     _receivedBodyBytes += got;
 
     if (_receivedBodyBytes == _bodySize) {
-      var body = _body;
+      var body = _body!;
 
       _bodySize = 0;
       _body = null;
@@ -134,7 +130,9 @@
 
       var completer = _frameCompleter;
       _frameCompleter = null;
-      completer.complete(Uint8List(0));
+      if (completer != null) {
+        completer.complete(Uint8List(0));
+      }
     } else {
       _registerForReadableBytes();
     }
@@ -154,7 +152,7 @@
       _registerForReadableBytes();
     }
 
-    return _frameCompleter.future;
+    return _frameCompleter!.future;
   }
 
   void _registerForReadableBytes() {
@@ -168,8 +166,9 @@
       _body = null;
       _receivedBodyBytes = 0;
       _frameCompleter = null;
-
-      completer.completeError(e);
+      if (completer != null) {
+        completer.completeError(e);
+      }
     });
   }
 }
diff --git a/lib/dart/lib/src/transport/t_http_transport.dart b/lib/dart/lib/src/transport/t_http_transport.dart
index 71fdb3c..cbd5242 100644
--- a/lib/dart/lib/src/transport/t_http_transport.dart
+++ b/lib/dart/lib/src/transport/t_http_transport.dart
@@ -33,9 +33,9 @@
   final THttpConfig config;
 
   THttpClientTransport(this.httpClient, this.config) {
-    if (httpClient == null) {
+    /*if (httpClient == null) {
       throw ArgumentError.notNull("httpClient");
-    }
+    }*/
   }
 
   @override
@@ -75,11 +75,11 @@
 class THttpConfig {
   final Uri url;
 
-  Map<String, String> _headers;
+  late Map<String, String> _headers;
   Map<String, String> get headers => _headers;
 
   THttpConfig(this.url, Map<String, String> headers) {
-    if (url == null || !url.hasAuthority) {
+    if (!url.hasAuthority) {
       throw ArgumentError("Invalid url");
     }
 
@@ -89,9 +89,7 @@
   void _initHeaders(Map<String, String> initial) {
     var h = {};
 
-    if (initial != null) {
       h.addAll(initial);
-    }
 
     h['Content-Type'] = 'application/x-thrift';
     h['Accept'] = 'application/x-thrift';
diff --git a/lib/dart/lib/src/transport/t_message_reader.dart b/lib/dart/lib/src/transport/t_message_reader.dart
index 620a27a..afef386 100644
--- a/lib/dart/lib/src/transport/t_message_reader.dart
+++ b/lib/dart/lib/src/transport/t_message_reader.dart
@@ -45,9 +45,9 @@
 class _TMessageReaderTransport extends TTransport {
   _TMessageReaderTransport();
 
-  Iterator<int> _readIterator;
+  Iterator<int>? _readIterator;
 
-  void reset(Uint8List bytes, [int offset = 0]) {
+  void reset(Uint8List? bytes, [int offset = 0]) {
     if (bytes == null) {
       _readIterator = null;
       return;
@@ -57,11 +57,7 @@
       throw ArgumentError("The offset exceeds the bytes length");
     }
 
-    _readIterator = bytes.iterator;
-
-    for (var i = 0; i < offset; i++) {
-      _readIterator.moveNext();
-    }
+    _readIterator = bytes.skip(offset).iterator;
   }
 
   @override
@@ -75,9 +71,6 @@
 
   @override
   int read(Uint8List buffer, int offset, int length) {
-    if (buffer == null) {
-      throw ArgumentError.notNull("buffer");
-    }
 
     if (offset + length > buffer.length) {
       throw ArgumentError("The range exceeds the buffer length");
@@ -88,8 +81,8 @@
     }
 
     int i = 0;
-    while (i < length && _readIterator.moveNext()) {
-      buffer[offset + i] = _readIterator.current;
+    while (i < length && _readIterator!.moveNext()) {
+      buffer[offset + i] = _readIterator!.current;
       i++;
     }
 
diff --git a/lib/dart/lib/src/transport/t_socket_transport.dart b/lib/dart/lib/src/transport/t_socket_transport.dart
index d9929c8..b568a8b 100644
--- a/lib/dart/lib/src/transport/t_socket_transport.dart
+++ b/lib/dart/lib/src/transport/t_socket_transport.dart
@@ -34,10 +34,6 @@
 
   /// A transport using the provided [socket].
   TSocketTransport(this.socket) {
-    if (socket == null) {
-      throw ArgumentError.notNull('socket');
-    }
-
     socket.onError.listen((e) => logger.warning(e));
     socket.onMessage.listen(handleIncomingMessage);
   }
@@ -96,7 +92,7 @@
 
     if (_completers.isNotEmpty) {
       var completer = _completers.removeAt(0);
-      completer.complete();
+      completer.complete(Uint8List(0));
     }
   }
 }
@@ -134,15 +130,13 @@
     var completer = Completer<Uint8List>.sync();
     _completers[seqid] = completer;
 
-    if (responseTimeout != null) {
-      Future.delayed(responseTimeout, () {
-        var completer = _completers.remove(seqid);
-        if (completer != null) {
-          completer.completeError(
-              TimeoutException("Response timed out.", responseTimeout));
-        }
-      });
-    }
+    Future.delayed(responseTimeout, () {
+      var completer = _completers.remove(seqid);
+      if (completer != null) {
+        completer.completeError(
+            TimeoutException("Response timed out.", responseTimeout));
+      }
+    });
 
     socket.send(bytes);
 
@@ -156,7 +150,7 @@
     TMessage message = messageReader.readMessage(messageBytes);
     var completer = _completers.remove(message.seqid);
     if (completer != null) {
-      completer.complete();
+      completer.complete(Uint8List(0));
     }
   }
 }
diff --git a/lib/dart/pubspec.yaml b/lib/dart/pubspec.yaml
index 732e553..c05d3d1 100644
--- a/lib/dart/pubspec.yaml
+++ b/lib/dart/pubspec.yaml
@@ -24,20 +24,20 @@
 documentation: http://thrift.apache.org
 
 environment:
-  sdk: ">=2.0.0 <3.0.0"
+  sdk: ">=2.12.0 <4.0.0"
 
 dependencies:
   fixnum: ">=0.10.2 <2.0.0"
-  http: ">=0.11.3 <0.14.0"
+  http: "^1.2.0"
   logging: ">=0.11.0 <2.0.0"
 
 dev_dependencies:
   build_runner: ">=1.7.1 <3.0.0"
   build_test: ">=0.10.9 <3.0.0"
-  build_vm_compilers: ^1.0.3
-  build_web_compilers: ">=2.7.1 <4.0.0"
-  dart_dev: ^3.0.0
-  dart_style: ">=1.3.1 <3.0.0"
+  build_vm_compilers: ^1.0.13
+  build_web_compilers: ^3.2.7
+  dart_dev: ^4.1.0
+  dart_style: ^2.3.0
   mockito: ">=4.1.1 <6.0.0"
   test: ^1.9.1
   workiva_analysis_options: ^1.0.0
\ No newline at end of file
diff --git a/lib/dart/test/protocol/t_protocol_test.dart b/lib/dart/test/protocol/t_protocol_test.dart
index 15d973c..4b6ca09 100644
--- a/lib/dart/test/protocol/t_protocol_test.dart
+++ b/lib/dart/test/protocol/t_protocol_test.dart
@@ -15,6 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
+
 library thrift.test.transport.t_json_protocol_test;
 
 import 'dart:async';
@@ -27,7 +28,7 @@
 void main() {
   final message = TMessage('my message', TMessageType.ONEWAY, 123);
 
-  TProtocol protocol;
+  late TProtocol protocol;
 
   Primitive getPrimitive(int tType) {
     switch (tType) {
@@ -69,7 +70,7 @@
     expect(output, input);
   }
 
-  Future primitiveNullTest(Primitive primitive) async {
+  /*Future primitiveNullTest(Primitive primitive) async {
     primitive.write(null);
     protocol.writeMessageEnd();
 
@@ -79,7 +80,7 @@
     var output = primitive.read();
 
     expect(output, primitive.defaultValue);
-  }
+  }*/
 
   var sharedTests = () {
     test('Test message', () async {
@@ -179,61 +180,61 @@
     test('Test bool', () async {
       await primitiveTest(getPrimitive(TType.BOOL), true);
     });
-
+/*
     test('Test bool null', () async {
       await primitiveNullTest(getPrimitive(TType.BOOL));
     });
-
+*/
     test('Test byte', () async {
       await primitiveTest(getPrimitive(TType.BYTE), 64);
     });
-
+/*
     test('Test byte null', () async {
       await primitiveNullTest(getPrimitive(TType.BYTE));
     });
-
+*/
     test('Test I16', () async {
       await primitiveTest(getPrimitive(TType.I16), 32767);
     });
-
+/*
     test('Test I16 null', () async {
       await primitiveNullTest(getPrimitive(TType.I16));
     });
-
+*/
     test('Test I32', () async {
       await primitiveTest(getPrimitive(TType.I32), 2147483647);
     });
-
+/*
     test('Test I32 null', () async {
       await primitiveNullTest(getPrimitive(TType.I32));
     });
-
+*/
     test('Test I64', () async {
       await primitiveTest(getPrimitive(TType.I64), 9223372036854775807);
     });
-
+/*
     test('Test I64 null', () async {
       await primitiveNullTest(getPrimitive(TType.I64));
     });
-
+*/
     test('Test double', () async {
       await primitiveTest(getPrimitive(TType.DOUBLE), 3.1415926);
     });
-
+/*
     test('Test double null', () async {
       await primitiveNullTest(getPrimitive(TType.DOUBLE));
     });
-
+*/
     test('Test string', () async {
       var input = 'There are only two hard things in computer science: '
           'cache invalidation, naming things, and off-by-one errors.';
       await primitiveTest(getPrimitive(TType.STRING), input);
     });
-
+/*
     test('Test string null', () async {
       await primitiveNullTest(getPrimitive(TType.STRING));
     });
-
+*/
     test('Test binary', () async {
       var input = Uint8List.fromList(List.filled(100, 123));
 
diff --git a/lib/dart/test/serializer/serializer_test.dart b/lib/dart/test/serializer/serializer_test.dart
index 89883cb..33df024 100644
--- a/lib/dart/test/serializer/serializer_test.dart
+++ b/lib/dart/test/serializer/serializer_test.dart
@@ -25,9 +25,9 @@
 
 void main() {
   var serializer = () {
-    TDeserializer deserializer;
-    TSerializer serializer;
-    TestTObject testTObject;
+    TDeserializer deserializer = TDeserializer();
+    TSerializer serializer = TSerializer();
+    TestTObject testTObject = TestTObject();
 
     setUp(() {
       serializer = TSerializer();
@@ -39,7 +39,7 @@
       testTObject.d = 15.25;
       testTObject.i = 10;
 
-      var testList = List<String>();
+      var testList = <String>[];
       testList.add("TEST 1");
       testList.add("TEST 2");
 
diff --git a/lib/dart/test/serializer/serializer_test_data.dart b/lib/dart/test/serializer/serializer_test_data.dart
index fc488f4..f5580ff 100644
--- a/lib/dart/test/serializer/serializer_test_data.dart
+++ b/lib/dart/test/serializer/serializer_test_data.dart
@@ -30,15 +30,15 @@
   static final TField _L_FIELD_DESC = TField("l", TType.LIST, 4);
   static final TField _B_FIELD_DESC = TField("b", TType.BOOL, 5);
 
-  int _i;
+  int? _i;
   static const int I = 1;
-  double _d;
+  double? _d;
   static const int D = 2;
-  String _s;
+  String? _s;
   static const int S = 3;
-  List<String> _l;
+  List<String>? _l;
   static const int L = 4;
-  bool _b;
+  bool? _b;
   static const int B = 5;
 
   bool __isset_i = false;
@@ -48,9 +48,9 @@
   TestTObject();
 
   // i
-  int get i => this._i;
+  int? get i => this._i;
 
-  set i(int i) {
+  set i(int? i) {
     this._i = i;
     this.__isset_i = true;
   }
@@ -62,9 +62,9 @@
   }
 
   // d
-  double get d => this._d;
+  double? get d => this._d;
 
-  set d(double d) {
+  set d(double? d) {
     this._d = d;
     this.__isset_d = true;
   }
@@ -76,9 +76,9 @@
   }
 
   // s
-  String get s => this._s;
+  String? get s => this._s;
 
-  set s(String s) {
+  set s(String? s) {
     this._s = s;
   }
 
@@ -89,9 +89,9 @@
   }
 
   // l
-  List<String> get l => this._l;
+  List<String>? get l => this._l;
 
-  set l(List<String> l) {
+  set l(List<String>? l) {
     this._l = l;
   }
 
@@ -102,9 +102,9 @@
   }
 
   // b
-  bool get b => this._b;
+  bool? get b => this._b;
 
-  set b(bool b) {
+  set b(bool? b) {
     this._b = b;
     this.__isset_b = true;
   }
@@ -134,13 +134,13 @@
   }
 
   @override
-  setFieldValue(int fieldID, Object value) {
+  setFieldValue(int fieldID, Object? value) {
     switch (fieldID) {
       case I:
         if (value == null) {
           unsetI();
         } else {
-          this.i = value;
+          this.i = value as int;
         }
         break;
 
@@ -148,7 +148,7 @@
         if (value == null) {
           unsetD();
         } else {
-          this.d = value;
+          this.d = value as double;
         }
         break;
 
@@ -156,7 +156,7 @@
         if (value == null) {
           unsetS();
         } else {
-          this.s = value;
+          this.s = value as String;
         }
         break;
 
@@ -172,7 +172,7 @@
         if (value == null) {
           unsetB();
         } else {
-          this.b = value;
+          this.b = value as bool;
         }
         break;
 
@@ -237,11 +237,11 @@
           if (field.type == TType.LIST) {
             {
               TList _list74 = iprot.readListBegin();
-              this.l = List<String>();
+              this.l = <String>[];
               for (int _i75 = 0; _i75 < _list74.length; ++_i75) {
                 String _elem76;
                 _elem76 = iprot.readString();
-                this.l.add(_elem76);
+                this.l?.add(_elem76);
               }
               iprot.readListEnd();
             }
@@ -275,21 +275,21 @@
 
     oprot.writeStructBegin(_STRUCT_DESC);
     oprot.writeFieldBegin(_I_FIELD_DESC);
-    oprot.writeI32(this.i);
+    oprot.writeI32(this.i!);
     oprot.writeFieldEnd();
     oprot.writeFieldBegin(_D_FIELD_DESC);
-    oprot.writeDouble(this.d);
+    oprot.writeDouble(this.d!);
     oprot.writeFieldEnd();
     if (this.s != null) {
       oprot.writeFieldBegin(_S_FIELD_DESC);
-      oprot.writeString(this.s);
+      oprot.writeString(this.s!);
       oprot.writeFieldEnd();
     }
     if (this.l != null) {
       oprot.writeFieldBegin(_L_FIELD_DESC);
       {
-        oprot.writeListBegin(TList(TType.STRING, this.l.length));
-        for (var elem77 in this.l) {
+        oprot.writeListBegin(TList(TType.STRING, this.l!.length));
+        for (var elem77 in this.l!) {
           oprot.writeString(elem77);
         }
         oprot.writeListEnd();
@@ -297,7 +297,7 @@
       oprot.writeFieldEnd();
     }
     oprot.writeFieldBegin(_B_FIELD_DESC);
-    oprot.writeBool(this.b);
+    oprot.writeBool(this.b!);
     oprot.writeFieldEnd();
     oprot.writeFieldStop();
     oprot.writeStructEnd();
diff --git a/lib/dart/test/t_application_error_test.dart b/lib/dart/test/t_application_error_test.dart
index 848ce56..cecefc7 100644
--- a/lib/dart/test/t_application_error_test.dart
+++ b/lib/dart/test/t_application_error_test.dart
@@ -21,7 +21,7 @@
 import 'package:thrift/thrift.dart';
 
 void main() {
-  TProtocol protocol;
+  late TProtocol protocol;
 
   setUp(() {
     protocol = TBinaryProtocol(TBufferedTransport());
diff --git a/lib/dart/test/transport/t_framed_transport_test.dart b/lib/dart/test/transport/t_framed_transport_test.dart
index d9a15a5..a028c6f 100644
--- a/lib/dart/test/transport/t_framed_transport_test.dart
+++ b/lib/dart/test/transport/t_framed_transport_test.dart
@@ -28,10 +28,10 @@
   group('TFramedTransport partial reads', () {
     final flushAwaitDuration = Duration(seconds: 10);
 
-    FakeReadOnlySocket socket;
-    TSocketTransport socketTransport;
-    TFramedTransport transport;
-    var messageAvailable;
+    late FakeReadOnlySocket socket;
+    late TSocketTransport socketTransport;
+    late TFramedTransport transport;
+    late bool messageAvailable;
 
     setUp(() {
       socket = FakeReadOnlySocket();
diff --git a/lib/dart/test/transport/t_http_transport_test.dart b/lib/dart/test/transport/t_http_transport_test.dart
index 13f0ee9..d0d91f4 100644
--- a/lib/dart/test/transport/t_http_transport_test.dart
+++ b/lib/dart/test/transport/t_http_transport_test.dart
@@ -34,8 +34,8 @@
   const utf8Codec = Utf8Codec();
 
   group('THttpClientTransport', () {
-    FakeHttpClient client;
-    THttpClientTransport transport;
+    late FakeHttpClient client;
+    late THttpClientTransport transport;
 
     setUp(() {
       client = FakeHttpClient(sync: false);
@@ -78,8 +78,8 @@
   });
 
   group('THttpClientTransport with multiple messages', () {
-    FakeHttpClient client;
-    THttpClientTransport transport;
+    late FakeHttpClient client;
+    late THttpClientTransport transport;
 
     setUp(() {
       client = FakeHttpClient(sync: true);
@@ -88,7 +88,7 @@
     });
 
     test('Test read correct buffer after flush', () async {
-      String bufferText;
+      String bufferText= "";
       var expectedText = 'response 1';
       var expectedBytes = utf8Codec.encode(expectedText);
 
@@ -124,8 +124,8 @@
 
   @override
   Future<Response> post(url,
-      {Map<String, String> headers, body, Encoding encoding}) {
-    postRequest = body;
+      {Map<String, String>? headers, body, Encoding? encoding}) {
+    postRequest = body.toString();
     var response = Response(postResponse, 200);
 
     if (sync) {
@@ -136,33 +136,34 @@
   }
 
   @override
-  Future<Response> head(url, {Map<String, String> headers}) =>
+  Future<Response> head(url, {Map<String, String>? headers}) =>
       throw UnimplementedError();
 
   @override
-  Future<Response> get(url, {Map<String, String> headers}) =>
+  Future<Response> get(url, {Map<String, String>? headers}) =>
       throw UnimplementedError();
 
   @override
   Future<Response> put(url,
-          {Map<String, String> headers, body, Encoding encoding}) =>
+          {Map<String, String>? headers, body, Encoding? encoding}) =>
       throw UnimplementedError();
 
   @override
   Future<Response> patch(url,
-          {Map<String, String> headers, body, Encoding encoding}) =>
+          {Map<String, String>? headers, body, Encoding? encoding}) =>
       throw UnimplementedError();
 
   @override
-  Future<Response> delete(url, {Map<String, String> headers}) =>
+  Future<Response> delete(Uri url,
+          {Map<String, String>? headers, Object? body, Encoding? encoding}) =>
       throw UnimplementedError();
 
   @override
-  Future<String> read(url, {Map<String, String> headers}) =>
+  Future<String> read(url, {Map<String, String>? headers}) =>
       throw UnimplementedError();
 
   @override
-  Future<Uint8List> readBytes(url, {Map<String, String> headers}) =>
+  Future<Uint8List> readBytes(url, {Map<String, String>? headers}) =>
       throw UnimplementedError();
 
   @override
diff --git a/lib/dart/test/transport/t_socket_transport_test.dart b/lib/dart/test/transport/t_socket_transport_test.dart
index d46f5ed..80edb68 100644
--- a/lib/dart/test/transport/t_socket_transport_test.dart
+++ b/lib/dart/test/transport/t_socket_transport_test.dart
@@ -39,8 +39,8 @@
   final framedResponseBase64 = base64.encode(_getFramedResponse(responseBytes));
 
   group('TClientSocketTransport', () {
-    FakeSocket socket;
-    TTransport transport;
+    late FakeSocket socket;
+    late TTransport transport;
 
     setUp(() async {
       socket = FakeSocket(sync: false);
@@ -74,8 +74,8 @@
   }, timeout: Timeout(Duration(seconds: 1)));
 
   group('TClientSocketTransport with FramedTransport', () {
-    FakeSocket socket;
-    TTransport transport;
+    late FakeSocket socket;
+    late TTransport transport;
 
     setUp(() async {
       socket = FakeSocket(sync: true);
@@ -87,7 +87,7 @@
     });
 
     test('Test client sending data over framed transport', () async {
-      String bufferText;
+      String bufferText = "";
 
       Future responseReady = transport.flush().then((_) {
         var buffer = Uint8List(responseBytes.length);
@@ -104,9 +104,9 @@
   }, timeout: Timeout(Duration(seconds: 1)));
 
   group('TAsyncClientSocketTransport', () {
-    FakeSocket socket;
-    FakeProtocolFactory protocolFactory;
-    TTransport transport;
+    late FakeSocket socket;
+    late FakeProtocolFactory protocolFactory;
+    late TTransport transport;
 
     setUp(() async {
       socket = FakeSocket(sync: true);
@@ -122,7 +122,7 @@
     });
 
     test('Test response correlates to correct request', () async {
-      String bufferText;
+      String bufferText = "";
 
       Future responseReady = transport.flush().then((_) {
         var buffer = Uint8List(responseBytes.length);
@@ -152,9 +152,9 @@
   }, timeout: Timeout(Duration(seconds: 1)));
 
   group('TAsyncClientSocketTransport with TFramedTransport', () {
-    FakeSocket socket;
-    FakeProtocolFactory protocolFactory;
-    TTransport transport;
+    late FakeSocket socket;
+    late FakeProtocolFactory protocolFactory;
+    late TTransport transport;
 
     setUp(() async {
       socket = FakeSocket(sync: true);
@@ -173,7 +173,7 @@
     });
 
     test('Test async client sending data over framed transport', () async {
-      String bufferText;
+      String bufferText = "";
 
       Future responseReady = transport.flush().then((_) {
         var buffer = Uint8List(responseBytes.length);
@@ -251,7 +251,7 @@
         _onErrorController = StreamController.broadcast(sync: sync),
         _onMessageController = StreamController.broadcast(sync: sync);
 
-  bool _isOpen;
+  bool _isOpen = false;
 
   @override
   bool get isOpen => _isOpen;
@@ -271,8 +271,8 @@
     _onStateController.add(TSocketState.CLOSED);
   }
 
-  Uint8List _sendPayload;
-  Uint8List get sendPayload => _sendPayload;
+  Uint8List? _sendPayload;
+  Uint8List? get sendPayload => _sendPayload;
 
   @override
   void send(Uint8List data) {
@@ -292,7 +292,7 @@
 class FakeProtocolFactory implements TProtocolFactory {
   FakeProtocolFactory();
 
-  TMessage message;
+  TMessage message = TMessage("", TMessageType.CALL, 0 /* seqid */);
 
   @override
   getProtocol(TTransport transport) => FakeProtocol(message);
diff --git a/lib/dart/test/transport/t_transport_test.dart b/lib/dart/test/transport/t_transport_test.dart
index 4758593..e60f61d 100644
--- a/lib/dart/test/transport/t_transport_test.dart
+++ b/lib/dart/test/transport/t_transport_test.dart
@@ -25,16 +25,19 @@
   group('TTransportFactory', () {
     test('transport is returned from base factory', () async {
       TTransport result;
-      TTransport transport;
+      TTransport? transport;
 
       var factory = TTransportFactory();
 
-      result = await factory.getTransport(transport);
-      expect(result, isNull);
+      try {
+        result = await factory.getTransport(transport!);
+        expect(result, isNull);
+      } catch (e) {
+        expect(e, isA<TypeError>());
+      }
 
-      transport = TBufferedTransport();
+      transport = TBufferedTransport()..open();
       result = await factory.getTransport(transport);
-
       expect(result, transport);
     });
   });
diff --git a/test/dart/Makefile.am b/test/dart/Makefile.am
index 3552668..835ac4a 100644
--- a/test/dart/Makefile.am
+++ b/test/dart/Makefile.am
@@ -17,6 +17,9 @@
 # under the License.
 #
 
+# Dart build command
+DARTPUB = dart pub
+
 gen-dart/thrift_test/lib/thrift_test.dart: ../v0.16/ThriftTest.thrift
 	$(THRIFT) --gen dart ../v0.16/ThriftTest.thrift
 
diff --git a/test/dart/test_client/bin/main.dart b/test/dart/test_client/bin/main.dart
index feba612..c06c09e 100644
--- a/test/dart/test_client/bin/main.dart
+++ b/test/dart/test_client/bin/main.dart
@@ -21,10 +21,10 @@
 
 import 'package:args/args.dart';
 import 'package:collection/collection.dart';
-import 'package:http/http.dart' as http;
 import 'package:thrift/thrift.dart';
 import 'package:thrift/thrift_console.dart';
 import 'package:thrift_test/thrift_test.dart';
+import 'package:http/io_client.dart';
 
 const TEST_BASETYPES = 1; // 0000 0001
 const TEST_STRUCTS = 2; // 0000 0010
@@ -53,13 +53,13 @@
   String toString() => '$actual != $expected';
 }
 
-List<TTest> _tests;
-ThriftTestClient client;
-bool verbose;
+late List<TTest> _tests;
+late ThriftTestClient client;
+late bool verbose;
 
 /// Adapted from TestClient.php
 main(List<String> args) async {
-  ArgResults results = _parseArgs(args);
+  ArgResults? results = _parseArgs(args);
 
   if (results == null) {
     exit(TEST_UNKNOWN);
@@ -99,7 +99,7 @@
   exit(result);
 }
 
-ArgResults _parseArgs(List<String> args) {
+ArgResults? _parseArgs(List<String> args) {
   var parser = new ArgParser();
   parser.addOption('host', defaultsTo: 'localhost', help: 'The server host');
   parser.addOption('port', defaultsTo: '9090', help: 'The port to connect to');
@@ -122,7 +122,7 @@
       });
   parser.addFlag('verbose', defaultsTo: true);
 
-  ArgResults results;
+  ArgResults? results;
   try {
     results = parser.parse(args);
   } catch (e) {
@@ -147,12 +147,12 @@
 }
 
 Future _initTestClient(
-    {String host, int port, String transportType, String protocolType}) async {
+    {required String host, required int port, required String transportType, required String protocolType}) async {
   TTransport transport;
   var protocolFactory = getProtocolFactory(protocolType);
 
   if (transportType == 'http') {
-    var httpClient = new http.IOClient();
+    var httpClient = IOClient();
     var uri = Uri.parse('http://$host:$port');
     var config = new THttpConfig(uri, {});
     transport = new THttpClientTransport(httpClient, config);
@@ -279,8 +279,8 @@
   }));
 
   tests.add(new TTest(TEST_CONTAINERS, 'testMapMap', () async {
-    Map<int, Map<int, int>> result = await client.testMapMap(1);
-    if (result.isEmpty || result[result.keys.first].isEmpty) {
+    Map<int, Map<int, int>>? result = await client.testMapMap(1);
+    if (result != null && (result.isEmpty || (result[result.keys.first] != null && result[result.keys.first]!.isEmpty))) {
       throw new TTestError(result, 'Map<int, Map<int, int>>');
     }
   }));
@@ -290,8 +290,8 @@
     input.userMap = {Numberz.FIVE: 5000};
     input.xtructs = [xtruct];
 
-    Map<int, Map<int, Insanity>> result = await client.testInsanity(input);
-    if (result.isEmpty || result[result.keys.first].isEmpty) {
+    Map<int, Map<int, Insanity>>? result = await client.testInsanity(input);
+    if (result != null &&  ( result.isEmpty || (result[result.keys.first] != null && result[result.keys.first]!.isEmpty))) {
       throw new TTestError(result, 'Map<int, Map<int, Insanity>>');
     }
   }));
diff --git a/test/dart/test_client/pubspec.yaml b/test/dart/test_client/pubspec.yaml
index e48da9f..3c747e5 100644
--- a/test/dart/test_client/pubspec.yaml
+++ b/test/dart/test_client/pubspec.yaml
@@ -22,11 +22,11 @@
 homepage: http://thrift.apache.org
 
 environment:
-  sdk: ">=1.24.3 <3.0.0"
+  sdk: ">=2.12.0 <4.0.0"
 
 dependencies:
-  args: ">=0.13.0 <2.0.0"
-  http: ^0.13.3
+  args: "^2.4.2"
+  http: ^1.2.0
   thrift:
     path: ../../../lib/dart
   thrift_test:
diff --git a/tutorial/dart/Makefile.am b/tutorial/dart/Makefile.am
index e29e3c5..17c16ab 100644
--- a/tutorial/dart/Makefile.am
+++ b/tutorial/dart/Makefile.am
@@ -19,6 +19,9 @@
 
 BUILT_SOURCES = gen-dart/tutorial/lib/tutorial.dart gen-dart/shared/lib/shared.dart
 
+# Use 'dart pub' if 'pub' command is not available
+DARTPUB = dart pub
+
 gen-dart/tutorial/lib/tutorial.dart gen-dart/shared/lib/shared.dart: $(top_srcdir)/tutorial/tutorial.thrift
 	$(THRIFT) --gen dart -r $<
 
diff --git a/tutorial/dart/client/pubspec.yaml b/tutorial/dart/client/pubspec.yaml
index a8c148d..4ffaa42 100644
--- a/tutorial/dart/client/pubspec.yaml
+++ b/tutorial/dart/client/pubspec.yaml
@@ -22,7 +22,7 @@
 homepage: http://thrift.apache.org
 
 environment:
-  sdk: ">=1.13.0 <3.0.0"
+  sdk: ">=2.12.0 <4.0.0"
 
 dependencies:
   shared:
diff --git a/tutorial/dart/client/web/client.dart b/tutorial/dart/client/web/client.dart
index 4f02d0d..be5cfe6 100644
--- a/tutorial/dart/client/web/client.dart
+++ b/tutorial/dart/client/web/client.dart
@@ -24,7 +24,7 @@
 
 /// Adapted from the AS3 tutorial
 void main() {
-  new CalculatorUI(querySelector('#output')).start();
+  new CalculatorUI(querySelector('#output') as DivElement ).start();
 }
 
 class CalculatorUI {
@@ -32,8 +32,8 @@
 
   CalculatorUI(this.output);
 
-  TTransport _transport;
-  Calculator _calculatorClient;
+  late TTransport _transport;
+  late Calculator _calculatorClient;
 
   void start() {
     _buildInterface();
@@ -120,12 +120,12 @@
   void _onAddClick(MouseEvent e) {
     _validate();
 
-    InputElement num1 = querySelector("#add1");
-    InputElement num2 = querySelector("#add2");
-    SpanElement result = querySelector("#addResult");
+    InputElement num1 = querySelector("#add1") as InputElement;
+    InputElement num2 = querySelector("#add2")as InputElement;
+    SpanElement result = querySelector("#addResult") as SpanElement;
 
     _calculatorClient
-        .add(int.parse(num1.value), int.parse(num2.value))
+        .add(int.parse(num1.value ?? "0"), int.parse(num2.value ?? "0"))
         .then((int n) {
       result.text = "$n";
     });
@@ -211,21 +211,21 @@
   void _onCalcClick(MouseEvent e) {
     _validate();
 
-    InputElement num1 = querySelector("#calc1");
-    InputElement num2 = querySelector("#calc2");
-    SelectElement op = querySelector("#calcOp");
-    SpanElement result = querySelector("#calcResult");
-    InputElement logId = querySelector("#logId");
-    InputElement comment = querySelector("#comment");
+    InputElement num1 = querySelector("#calc1") as InputElement;
+    InputElement num2 = querySelector("#calc2")as InputElement;
+    SelectElement op = querySelector("#calcOp") as SelectElement;
+    SpanElement result = querySelector("#calcResult") as SpanElement;
+    InputElement logId = querySelector("#logId") as InputElement;
+    InputElement comment = querySelector("#comment") as InputElement;
 
-    int logIdValue = int.parse(logId.value);
+    int logIdValue = int.parse(logId.value ?? "0");
     logId.value = (logIdValue + 1).toString();
 
     Work work = new Work();
-    work.num1 = int.parse(num1.value);
-    work.num2 = int.parse(num2.value);
-    work.op = int.parse(op.options[op.selectedIndex].value);
-    work.comment = comment.value;
+    work.num1 = int.parse(num1.value!);
+    work.num2 = int.parse(num2.value!);
+    work.op = int.parse(op.options[op.selectedIndex!].value);
+    work.comment = comment.value!;
 
     _calculatorClient.calculate(logIdValue, work).then((int n) {
       result.text = "$n";
@@ -266,13 +266,13 @@
   void _onGetStructClick(MouseEvent e) {
     _validate();
 
-    InputElement structKey = querySelector("#structKey");
-    TextAreaElement result = querySelector("#getStructResult");
+    InputElement structKey = querySelector("#structKey") as InputElement;
+    TextAreaElement result = querySelector("#getStructResult") as TextAreaElement;
 
     _calculatorClient
-        .getStruct(int.parse(structKey.value))
-        .then((SharedStruct s) {
-      result.text = "${s.toString()}";
+        .getStruct(int.parse(structKey.value!))
+        .then((SharedStruct? s) {
+      result.text = "${s?.toString()}";
     });
   }
 }
diff --git a/tutorial/dart/console_client/bin/main.dart b/tutorial/dart/console_client/bin/main.dart
index fda206a..992c3f8 100644
--- a/tutorial/dart/console_client/bin/main.dart
+++ b/tutorial/dart/console_client/bin/main.dart
@@ -24,8 +24,8 @@
 import 'package:thrift/thrift_console.dart';
 import 'package:tutorial/tutorial.dart';
 
-TTransport _transport;
-Calculator _calculator;
+late TTransport _transport;
+late Calculator _calculator;
 int logid = 0;
 
 const Map<String, int> operationLookup = const {
@@ -44,7 +44,7 @@
   var parser = new ArgParser();
   parser.addOption('port', defaultsTo: '9090', help: 'The port to connect to');
 
-  ArgResults results;
+  ArgResults? results;
   try {
     results = parser.parse(args);
   } catch (e) {
@@ -77,9 +77,9 @@
   while (true) {
     stdout.write("> ");
     var input = stdin.readLineSync();
-    var parts = input.split(' ');
-    var command = parts[0];
-    var args = parts.length > 1 ? parts.sublist(1) : [];
+    var parts = input?.split(' ');
+    var command = parts?[0];
+    var args = parts!.length > 1 ? parts.sublist(1) : [];
 
     switch (command) {
       case 'ping':
@@ -91,7 +91,7 @@
         break;
 
       case 'calc':
-        int op = operationLookup[args[1]];
+        int? op = operationLookup[args[1]];
         if (!Operation.VALID_VALUES.contains(op)) {
           stdout.writeln('Unknown operator ${args[1]}');
           break;
@@ -99,7 +99,7 @@
 
         var work = new Work()
           ..num1 = int.parse(args[0])
-          ..op = op
+          ..op = op!
           ..num2 = int.parse(args[2])
           ..comment = args.length > 3 ? args[3] : '';
 
diff --git a/tutorial/dart/console_client/pubspec.yaml b/tutorial/dart/console_client/pubspec.yaml
index 0e2389b..9125e23 100644
--- a/tutorial/dart/console_client/pubspec.yaml
+++ b/tutorial/dart/console_client/pubspec.yaml
@@ -23,10 +23,10 @@
 homepage: http://thrift.apache.org
 
 environment:
-  sdk: ">=1.13.0 <3.0.0"
+  sdk: ">=2.12.0 <4.0.0"
 
 dependencies:
-  args: ">=0.13.0 <2.0.0"
+  args: ^2.4.2
   collection: ^1.1.0
   shared:
     path: ../gen-dart/shared
diff --git a/tutorial/dart/server/bin/main.dart b/tutorial/dart/server/bin/main.dart
index b8ac30d..3926777 100644
--- a/tutorial/dart/server/bin/main.dart
+++ b/tutorial/dart/server/bin/main.dart
@@ -25,9 +25,9 @@
 import 'package:tutorial/tutorial.dart';
 import 'package:shared/shared.dart';
 
-TProtocol _protocol;
-TProcessor _processor;
-WebSocket _webSocket;
+late TProtocol _protocol;
+late TProcessor _processor;
+late WebSocket _webSocket;
 
 main(List<String> args) {
   Logger.root.level = Level.ALL;
@@ -38,12 +38,12 @@
   var parser = new ArgParser();
   parser.addOption('port', defaultsTo: '9090', help: 'The port to listen on');
   parser.addOption('type',
-      defaultsTo: 'ws',
+      defaultsTo: 'tcp',
       allowed: ['ws', 'tcp'],
       help: 'The type of socket',
       allowedHelp: {'ws': 'WebSocket', 'tcp': 'TCP Socket'});
 
-  ArgResults results;
+  ArgResults? results;
   try {
     results = parser.parse(args);
   } catch (e) {
@@ -114,12 +114,12 @@
     return num1 + num2;
   }
 
-  Future<int> calculate(int logid, Work work) async {
+  Future<int> calculate(int logid, Work? work) async {
     print('calulate($logid, ${work.toString()})');
 
-    int val;
+    late int val;
 
-    switch (work.op) {
+    switch (work!.op) {
       case Operation.ADD:
         val = work.num1 + work.num2;
         break;
@@ -155,7 +155,7 @@
     print('zip()');
   }
 
-  Future<SharedStruct> getStruct(int key) async {
+  Future<SharedStruct?> getStruct(int key) async {
     print('getStruct($key)');
 
     return _log[key];
diff --git a/tutorial/dart/server/pubspec.yaml b/tutorial/dart/server/pubspec.yaml
index 3cca54e..e15874a 100644
--- a/tutorial/dart/server/pubspec.yaml
+++ b/tutorial/dart/server/pubspec.yaml
@@ -22,10 +22,10 @@
 homepage: http://thrift.apache.org
 
 environment:
-  sdk: ">=1.13.0 <3.0.0"
+  sdk: ">=2.12.0 <4.0.0"
 
 dependencies:
-  args: ">=0.13.0 <2.0.0"
+  args: "^2.4.2"
   shared:
     path: ../gen-dart/shared
   thrift: