diff --git a/gnu/local.mk b/gnu/local.mk
index a6c949f574..5ad14f18ef 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -907,6 +907,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/cyrus-sasl-ac-try-run-fix.patch		\
   %D%/packages/patches/dart-2.0.0-dev.8-disable-analytics.patch	\
   %D%/packages/patches/dart-2.0.0-dev.65-compile-with-dev.54.patch	\
+  %D%/packages/patches/dart-2.4.0-fix-build-with-2.1.patch	\
   %D%/packages/patches/date-output-pkg-config-files.patch	\
   %D%/packages/patches/datefudge-gettimeofday.patch		\
   %D%/packages/patches/dbacl-include-locale.h.patch		\
diff --git a/gnu/packages/dart.scm b/gnu/packages/dart.scm
index 38c2484eb4..33e0135e9f 100644
--- a/gnu/packages/dart.scm
+++ b/gnu/packages/dart.scm
@@ -986,3 +986,78 @@
     (native-inputs
      (alist-replace "dart" `(,dart-2.1.0-dev.5.0)
                     (package-native-inputs dart-2.1.0-dev.5.0)))))
+
+(define-public dart-2.4.0
+  (package
+    (inherit dart-2.1.0-dev.6.0)
+    (version "2.4.0")
+    (source
+     (origin
+       (method git-fetch)
+       (uri (git-reference
+             (url "https://github.com/dart-lang/sdk")
+             (commit version)))
+       (sha256
+        (base32
+         "0akm53mfxn3vrs512ml4qyljw2yw92g7mdszcx96hw7zr21d15s2"))
+       (patches
+        (list (search-patch "dart-2.4.0-fix-build-with-2.1.patch")))))
+    (arguments
+     (substitute-keyword-arguments (package-arguments dart-2.1.0-dev.6.0)
+       ((#:phases phases)
+        `(modify-phases ,phases
+           (add-before 'configure 'fix-terminal-color-detection
+             ;; Instead of trying to run bin/sh and check for tty, just
+             ;; disable color supports
+             (lambda _
+               (substitute*
+                   "pkg/front_end/lib/src/api_prototype/terminal_color_support.dart"
+                 (("/bin/sh") "false"))))
+           (add-after 'add-third-party-src 'add-icu
+             (lambda* (#:key inputs #:allow-other-keys)
+               (copy-recursively (assoc-ref inputs "icu")
+                                 "third_party/icu")))
+           (add-before 'configure 'remove-fuzzer-no-link
+             (lambda _
+               (substitute* "runtime/BUILD.gn"
+                 ((",fuzzer-no-link") ""))))
+           (replace 'patch-dart-action
+             ;; Path changed in this version
+             (lambda* (#:key inputs propagated-inputs #:allow-other-keys)
+               (substitute* "build/dart/dart_action.gni"
+                 ;; FIX: assignment had no effect
+                 (("dfe =") "# dfe =")
+                 ((".*dart_root/tools/sdks/dart-sdk/bin/snapshots.*" all)
+                  (string-append "# " all)))))))))
+    (inputs
+     (replace-inputs
+      dart-2.1.0-dev.6.0
+      `(("zlib"
+         ,(dart-pkg
+           "zlib" "c44fb7248079cc3d5563b14b3f758aee60d6b415"
+           "1r14mnrm7zmz2afp92fqdfbcr5gpjvcy46fs7s4qqrzspkjnpwik"
+           "https://chromium.googlesource.com/chromium/src/third_party/zlib"))
+        ("dart-pkg-bazel-worker"
+         ,(dart-pkg "bazel-worker" "bazel_worker-v0.1.20"
+                    "02g4cycbrwr833qkjj4dcq7n9alkq4xmkdrxpmjdjv54ilxg5xx9"))
+        ("dart-pkg-dart2js-info"
+         ,(dart-pkg "dart2js-info" "0.6.0"
+                    "1cirqph6yr1dn07979v1p2dyhn01r2c32w2k5ndpkjk7z9cx0bbr"))
+        ("dart-pkg-html"
+         ,(dart-pkg "html" "0.14.0+1"
+                    "0kf290mhpr1bklsgc35inpqafhc3wm8amh5a6933y3jiw2dgi94k"))
+        ("dart-pkg-linter"
+         ,(dart-pkg "linter" "0.1.91"
+                    "0slmsgm0ficwd85ljqxkzi64jlcwpkzwlnyfcx46plmnzxjvbmbc"))
+        ("dart-pkg-protobuf"
+         ,(dart-pkg "protobuf" "7d34c9e4e552a4f66acce32e4344ae27756a1949"
+                    "0ksfqq6a7xbivalwl7knbm7f7ihv8pq19d4j6rwffqdnh9wqza42"))
+        ("dart-pkgtested-dart-style"
+         ,(dart-pkg "dart-style" "1.2.8"
+            "1km62cgp0fyc5zxliq2ny6bzxj2amnjhkkc2rm06x1fv53vll26n")))))
+    (native-inputs
+     (alist-replace
+      "dart" `(,dart-2.1.0-dev.6.0)
+      (alist-replace
+       "gcc" `(,gcc-8)
+       (package-native-inputs dart-2.1.0-dev.6.0))))))
diff --git a/gnu/packages/patches/dart-2.4.0-fix-build-with-2.1.patch b/gnu/packages/patches/dart-2.4.0-fix-build-with-2.1.patch
new file mode 100644
index 0000000000..f9a43e2c25
--- /dev/null
+++ b/gnu/packages/patches/dart-2.4.0-fix-build-with-2.1.patch
@@ -0,0 +1,340 @@
+From 05739627950567885293f42fc4d4661be5e1ac04 Mon Sep 17 00:00:00 2001
+From: nixo <nicolo@nixo.xyz>
+Date: Tue, 24 Nov 2020 10:27:55 +0100
+Subject: [PATCH] Replace unsupported '...' and 'if' in lists
+
+---
+ .../lib/src/analyzer/code_generator.dart      |  27 +--
+ .../lib/src/compiler/shared_compiler.dart     |   9 +-
+ pkg/dev_compiler/lib/src/kernel/compiler.dart | 169 ++++++++++--------
+ 3 files changed, 121 insertions(+), 84 deletions(-)
+
+diff --git a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
+index 3ff97b0df18..8ab1afa6855 100644
+--- a/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
++++ b/pkg/dev_compiler/lib/src/analyzer/code_generator.dart
+@@ -4161,19 +4161,24 @@ class CodeGenerator extends Object
+     }
+ 
+     var location = _getLocation(condition.offset);
+-    return js.statement(' if (!#) #.assertFailed(#, #, #, #, #);', [
++    var newvar = [
+       jsCondition,
+       runtimeModule,
+-      if (message == null)
+-        JS.LiteralNull()
+-      else
+-        _visitExpression(message),
+-      js.escapedString(location.sourceUrl.toString()),
+-      // Lines and columns are typically printed with 1 based indexing.
+-      js.number(location.line + 1),
+-      js.number(location.column + 1),
+-      js.escapedString(condition.toSource()),
+-    ]);
++    ];
++
++    if (message == null) {
++      newvar.add(JS.LiteralNull());
++    } else {
++      newvar.add(_visitExpression(message));
++    }
++    newvar.addAll([
++        js.escapedString(location.sourceUrl.toString()),
++        // Lines and columns are typically printed with 1 based indexing.
++        js.number(location.line + 1),
++        js.number(location.column + 1),
++        js.escapedString(condition.toSource()),]);
++
++    return js.statement(' if (!#) #.assertFailed(#, #, #, #, #);', newvar);
+   }
+ 
+   @override
+diff --git a/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart b/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart
+index 6a3182d0607..86741493a69 100644
+--- a/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart
++++ b/pkg/dev_compiler/lib/src/compiler/shared_compiler.dart
+@@ -213,8 +213,13 @@ abstract class SharedCompiler<Library, Class, InterfaceType, FunctionNode> {
+   ///     dart.asInt(<expr>)
+   ///
+   @protected
+-  JS.Expression runtimeCall(String code, [List<Object> args]) =>
+-      js.call('#.$code', <Object>[runtimeModule, ...?args]);
++  JS.Expression runtimeCall(String code, [List<Object> args]) {
++    var obj = <Object>[runtimeModule];
++    if (args != null) {
++      obj.addAll(args);
++    }
++    return js.call('#.$code', obj);
++  }
+ 
+   /// Calls [runtimeCall] and uses `toStatement()` to convert the resulting
+   /// expression into a statement.
+diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
+index 531ca405cff..81424212e4c 100644
+--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
++++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
+@@ -554,9 +554,10 @@ class ProgramCompiler extends Object
+ 
+     var genericArgs = [
+       typeConstructor,
+-      if (deferredBaseClass != null && deferredBaseClass.isNotEmpty)
+-        js.call('(#) => { #; }', [jsFormals, deferredBaseClass]),
+     ];
++    if (deferredBaseClass != null && deferredBaseClass.isNotEmpty) {
++      genericArgs.add(js.call('(#) => { #; }', [jsFormals, deferredBaseClass]));
++    }
+ 
+     var genericCall = runtimeCall('generic(#)', [genericArgs]);
+ 
+@@ -726,11 +727,14 @@ class ProgramCompiler extends Object
+         var jsParams = _emitParameters(ctor.function);
+         _currentUri = savedUri;
+         var name = ctor.name.name;
+-        var ctorBody = [
+-          if (mixinCtor != null) mixinCtor,
+-          if (name != '' || hasUnnamedSuper)
+-            _emitSuperConstructorCall(className, name, jsParams),
++        var ctorBody = <JS.Statement>[
+         ];
++        if (mixinCtor != null) {
++          ctorBody.add(mixinCtor);
++        }
++        if (name != '' || hasUnnamedSuper) {
++          ctorBody.add(_emitSuperConstructorCall(className, name, jsParams));
++        }
+         body.add(_addConstructorToClass(
+             c, className, name, JS.Fun(jsParams, JS.Block(ctorBody))));
+       }
+@@ -1294,10 +1298,10 @@ class ProgramCompiler extends Object
+ 
+     if (emitMetadata) {
+       var constructors = <JS.Property>[];
+-      var allConstructors = [
+-        ...c.constructors,
+-        ...c.procedures.where((p) => p.isFactory),
+-      ];
++      var allConstructors = [ ];
++      allConstructors.addAll(c.constructors);
++      allConstructors.addAll(c.procedures.where((p) => p.isFactory));
++
+       for (var ctor in allConstructors) {
+         var memberName = _constructorName(ctor.name.name);
+         var type = _emitAnnotatedFunctionType(
+@@ -3032,10 +3036,13 @@ class ProgramCompiler extends Object
+       // (sync*/async/async*). Our code generator assumes it can emit names for
+       // named argument initialization, and sync* functions also emit locally
+       // modified parameters into the function's scope.
+-      var parameterNames = {
+-        for (var p in f.positionalParameters) p.name,
+-        for (var p in f.namedParameters) p.name,
+-      };
++      var parameterNames = Set<String>();
++      for (var p in f.positionalParameters) {
++        parameterNames.add(p.name);
++      }
++      for (var p in f.namedParameters) {
++        parameterNames.add(p.name);
++      }
+ 
+       return jsBody.toScopedBlock(parameterNames);
+     }
+@@ -3205,23 +3212,27 @@ class ProgramCompiler extends Object
+     }
+ 
+     var encodedConditionSource = node
+-        .enclosingComponent.uriToSource[node.location.file].source
+-        .sublist(node.conditionStartOffset, node.conditionEndOffset);
++    .enclosingComponent.uriToSource[node.location.file].source
++    .sublist(node.conditionStartOffset, node.conditionEndOffset);
+     var conditionSource = utf8.decode(encodedConditionSource);
+     var location = _getLocation(node.conditionStartOffset);
+-    return js.statement(' if (!#) #.assertFailed(#, #, #, #, #);', [
++    var newvar = [
+       jsCondition,
+       runtimeModule,
+-      if (node.message == null)
+-        JS.LiteralNull()
+-      else
+-        _visitExpression(node.message),
+-      js.escapedString(location.sourceUrl.toString()),
+-      // Lines and columns are typically printed with 1 based indexing.
+-      js.number(location.line + 1),
+-      js.number(location.column + 1),
+-      js.escapedString(conditionSource),
+-    ]);
++    ];
++    if (node.message == null) {
++      newvar.add(JS.LiteralNull());
++    } else {
++      newvar.add(_visitExpression(node.message));
++    }
++    newvar.addAll([
++        js.escapedString(location.sourceUrl.toString()),
++        // Lines and columns are typically printed with 1 based indexing.
++        js.number(location.line + 1),
++        js.number(location.column + 1),
++        js.escapedString(conditionSource)]);
++
++    return js.statement(' if (!#) #.assertFailed(#, #, #, #, #);', newvar);
+   }
+ 
+   static bool isBreakable(Statement stmt) {
+@@ -3624,15 +3635,17 @@ class ProgramCompiler extends Object
+         _emitVariableDef(exceptionParameter),
+         runtimeModule,
+         _emitVariableRef(caughtError)
+-      ]),
+-      if (stackTraceParameter != null)
+-        js.statement('let # = #.stackTrace(#)', [
++  ]) ];
++
++  if (stackTraceParameter != null) {
++    catchStatements.add(js.statement('let # = #.stackTrace(#)', [
+           _emitVariableDef(stackTraceParameter),
+           runtimeModule,
+           _emitVariableRef(caughtError)
+-        ]),
+-      catchBody,
+-    ];
++    ]));
++  }
++  catchStatements.add(catchBody);
++
+     _rethrowParameter = savedRethrow;
+     return JS.Catch(_emitVariableDef(caughtError), JS.Block(catchStatements));
+   }
+@@ -4425,12 +4438,14 @@ class ProgramCompiler extends Object
+             isGetter: !setter, isSetter: setter);
+       } else {
+         var function = member.function;
+-        var params = [
+-          ..._emitTypeFormals(function.typeParameters),
+-          for (var param in function.positionalParameters)
+-            JS.Identifier(param.name),
+-          if (function.namedParameters.isNotEmpty) namedArgumentTemp,
+-        ];
++        var params = [ ]..addAll(_emitTypeFormals(function.typeParameters));
++        for (var param in function.positionalParameters) {
++          params.add(JS.Identifier(param.name));
++        }
++        
++        if (function.namedParameters.isNotEmpty) {
++          params.add(namedArgumentTemp);
++        }
+ 
+         var fn = js.fun(
+             'function(#) { return super[#](#); }', [params, jsName, params]);
+@@ -4543,18 +4558,24 @@ class ProgramCompiler extends Object
+   List<JS.Expression> _emitArgumentList(Arguments node,
+       {bool types = true, Member target}) {
+     types = types && _reifyGenericFunction(target);
+-    return [
+-      if (types) for (var typeArg in node.types) _emitType(typeArg),
+-      for (var arg in node.positional)
+-        if (arg is StaticInvocation &&
+-            isJSSpreadInvocation(arg.target) &&
+-            arg.arguments.positional.length == 1)
+-          JS.Spread(_visitExpression(arg.arguments.positional[0]))
+-        else
+-          _visitExpression(arg),
+-      if (node.named.isNotEmpty)
+-        JS.ObjectInitializer(node.named.map(_emitNamedExpression).toList()),
+-    ];
++    var newvar = <JS.Expression>[];
++    if (types) {
++      for (var typeArg in node.types) {
++        newvar.add(_emitType(typeArg));
++      }
++    }
++    for (var arg in node.positional) {
++      if (arg is StaticInvocation && isJSSpreadInvocation(arg.target) &&
++        arg.arguments.positional.length == 1) {
++        newvar.add(JS.Spread(_visitExpression(arg.arguments.positional[0])));
++      } else {
++        newvar.add(_visitExpression(arg));
++      }
++    }
++    if (node.named.isNotEmpty) {
++      newvar.add(JS.ObjectInitializer(node.named.map(_emitNamedExpression).toList()));
++    }
++    return newvar;
+   }
+ 
+   JS.Property _emitNamedExpression(NamedExpression arg) {
+@@ -5052,12 +5073,14 @@ class ProgramCompiler extends Object
+ 
+   @override
+   JS.Expression visitMapLiteral(MapLiteral node) {
+-    var entries = [
+-      for (var e in node.entries) ...[
+-        _visitExpression(e.key),
+-        _visitExpression(e.value),
+-      ],
+-    ];
++    var entries = <JS.Expression>[ ];
++
++    for (var e in node.entries) {
++      entries.addAll([
++          _visitExpression(e.key),
++          _visitExpression(e.value),
++      ]);
++    }
+ 
+     // TODO(markzipan): remove const check when we use front-end const eval
+     if (!node.isConst) {
+@@ -5152,10 +5175,12 @@ class ProgramCompiler extends Object
+   @override
+   JS.Expression visitBlockExpression(BlockExpression node) {
+     var jsExpr = _visitExpression(node.value);
+-    var jsStmts = [
+-      for (var s in node.body.statements) _visitStatement(s),
+-      JS.Return(jsExpr),
+-    ];
++    var jsStmts = [ ];
++    for (var s in node.body.statements) {
++      jsStmts.add(_visitStatement(s));
++    }
++    jsStmts.add(JS.Return(jsExpr));
++
+     var jsBlock = JS.Block(jsStmts);
+     // BlockExpressions with async operations must be constructed
+     // with a generator instead of a lambda.
+@@ -5277,12 +5302,14 @@ class ProgramCompiler extends Object
+ 
+   @override
+   JS.Expression visitMapConstant(MapConstant node) {
+-    var entries = [
+-      for (var e in node.entries) ...[
+-        visitConstant(e.key),
+-        visitConstant(e.value),
+-      ],
+-    ];
++    var entries = [ ];
++    for (var e in node.entries) {
++      entries.addAll([
++          visitConstant(e.key),
++          visitConstant(e.value),
++      ]);
++    }
++  
+     return _emitConstMap(node.keyType, node.valueType, entries);
+   }
+ 
+@@ -5305,10 +5332,10 @@ class ProgramCompiler extends Object
+ 
+     var type = visitInterfaceType(node.getType(types) as InterfaceType);
+     var prototype = js.call("#.prototype", [type]);
+-    var properties = [
+-      JS.Property(propertyName("__proto__"), prototype),
+-      for (var e in node.fieldValues.entries) entryToProperty(e),
+-    ];
++    var properties = [ JS.Property(propertyName("__proto__"), prototype) ];
++    for (var e in node.fieldValues.entries) {
++      properties.add(entryToProperty(e));
++    }
+     return canonicalizeConstObject(
+         JS.ObjectInitializer(properties, multiline: true));
+   }
+-- 
+2.29.2
+
