Drop flutter submodule (#653)

diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml
index 8da7707..fd5c0ae 100644
--- a/.github/workflows/dart.yml
+++ b/.github/workflows/dart.yml
@@ -38,9 +38,8 @@
         run: dart analyze --fatal-infos .
       - name: Prepare Flutter
         run: |
-          git submodule init
-          git submodule update
-          export PATH=$PATH:$PWD/flutter/bin
+          dart run tool/update_sdk.dart
+          export PATH=$PATH:$PWD/flutter-sdk/bin
           flutter doctor -v
           flutter config --enable-web
       - name: Run tests
diff --git a/.gitmodules b/.gitmodules
index 37aa38f..e69de29 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +0,0 @@
-[submodule "flutter"]
-	path = flutter
-	url = https://github.com/flutter/flutter.git
diff --git a/.gitpod.yml b/.gitpod.yml
index 6205d4d..d2bf753 100644
--- a/.gitpod.yml
+++ b/.gitpod.yml
@@ -16,11 +16,3 @@
   - port: 9504
     onOpen: "ignore"
 
-# List the start up tasks. You can start them in parallel in multiple terminals. See https://www.gitpod.io/docs/config-start-tasks/
-tasks:
-  - init: ./flutter/bin/flutter doctor -v && pub get && grind deploy
-
-# Add the DartCode extension. See https://www.gitpod.io/docs/vscode-extensions/
-vscode:
-  extensions:
-    - Dart-Code.dart-code@3.9.0:umvoqCQMs3NpksmwhVtStw==
diff --git a/Dockerfile b/Dockerfile
index 2eeac87..119ee83 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,13 +1,6 @@
 # Keep aligned with min SDK in pubspec.yaml and .github/workflows/dart.yml.
 FROM google/dart:2.10.4
 
-# The specific commit that dart-services should use. This should be kept
-# in sync with the flutter submodule in the dart-services repo.
-# To retrieve this value, please run the following in your closest shell:
-#
-# $ (cd flutter && git rev-parse HEAD)
-ARG FLUTTER_COMMIT=5d36f2e7f5387b6c751449258ade8e4e6edf99be
-
 # We install unzip and remove the apt-index again to keep the
 # docker image diff small.
 RUN apt-get update && \
@@ -31,18 +24,11 @@
 
 ENV PATH="/home/dart/.pub-cache/bin:${PATH}"
 
-# Clone the flutter repo and set it to the same commit as the flutter submodule.
-RUN git clone https://github.com/flutter/flutter.git
-RUN cd flutter && git checkout $FLUTTER_COMMIT
-
 # Set the Flutter SDK up for web compilation.
-RUN flutter/bin/flutter doctor
-RUN flutter/bin/flutter config --enable-web
-RUN flutter/bin/flutter precache --web --no-android --no-ios --no-linux \
-  --no-windows --no-macos --no-fuchsia
+RUN dart pub run grinder setup-flutter-sdk
 
 # Build the dill file
-RUN pub run grinder build-storage-artifacts validate-storage-artifacts
+RUN dart pub run grinder build-storage-artifacts validate-storage-artifacts
 
 EXPOSE 8080
 
diff --git a/cloud_run.Dockerfile b/cloud_run.Dockerfile
index 561bc9f..c449ff3 100644
--- a/cloud_run.Dockerfile
+++ b/cloud_run.Dockerfile
@@ -1,13 +1,6 @@
 # Keep aligned with min SDK in pubspec.yaml and Dart test version in .travis.yml
 FROM google/dart:2.10.4
 
-# The specific commit that dart-services should use. This should be kept
-# in sync with the flutter submodule in the dart-services repo.
-# To retrieve this value, please run the following in your closest shell:
-#
-# $ (cd flutter && git rev-parse HEAD)
-ARG FLUTTER_COMMIT=5d36f2e7f5387b6c751449258ade8e4e6edf99be
-
 # We install unzip and remove the apt-index again to keep the
 # docker image diff small.
 RUN apt-get update && \
@@ -32,18 +25,11 @@
 
 ENV PATH="/home/dart/.pub-cache/bin:${PATH}"
 
-# Clone the flutter repo and set it to the same commit as the flutter submodule.
-RUN git clone https://github.com/flutter/flutter.git
-RUN cd flutter && git checkout $FLUTTER_COMMIT
-
 # Set the Flutter SDK up for web compilation.
-RUN flutter/bin/flutter doctor
-RUN flutter/bin/flutter config --enable-web
-RUN flutter/bin/flutter precache --web --no-android --no-ios --no-linux \
-  --no-windows --no-macos --no-fuchsia
+RUN dart pub run grinder setup-flutter-sdk
 
 # Build the dill file
-RUN pub run grinder build-storage-artifacts validate-storage-artifacts
+RUN dart pub run grinder build-storage-artifacts validate-storage-artifacts
 
 # Clear out any arguments the base images might have set and ensure we start
 # the Dart app using custom script enabling debug modes.
diff --git a/flutter b/flutter
deleted file mode 160000
index 5d36f2e..0000000
--- a/flutter
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 5d36f2e7f5387b6c751449258ade8e4e6edf99be
diff --git a/flutter-sdk-version.yaml b/flutter-sdk-version.yaml
index e1fb793..0073100 100644
--- a/flutter-sdk-version.yaml
+++ b/flutter-sdk-version.yaml
@@ -11,5 +11,5 @@
 # In order to update the SDK manually, run 'dart tool/update_sdk.dart'.
 
 flutter_sdk:
-  channel: beta
-  # version: 1.25.0-8.1.pre
+  # channel: beta
+  version: 1.25.0-8.3.pre
diff --git a/lib/src/protos/dart_services.pb.dart b/lib/src/protos/dart_services.pb.dart
index c9ee757..f718157 100644
--- a/lib/src/protos/dart_services.pb.dart
+++ b/lib/src/protos/dart_services.pb.dart
@@ -2,7 +2,7 @@
 //  Generated code. Do not modify.
 //  source: protos/dart_services.proto
 //
-// @dart = 2.3
+// @dart = 2.7
 // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
 
 import 'dart:core' as $core;
@@ -33,7 +33,19 @@
     ..hasRequiredFields = false;
 
   CompileRequest._() : super();
-  factory CompileRequest() => create();
+  factory CompileRequest({
+    $core.String source,
+    $core.bool returnSourceMap,
+  }) {
+    final _result = create();
+    if (source != null) {
+      _result.source = source;
+    }
+    if (returnSourceMap != null) {
+      _result.returnSourceMap = returnSourceMap;
+    }
+    return _result;
+  }
   factory CompileRequest.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -104,7 +116,15 @@
     ..hasRequiredFields = false;
 
   CompileDDCRequest._() : super();
-  factory CompileDDCRequest() => create();
+  factory CompileDDCRequest({
+    $core.String source,
+  }) {
+    final _result = create();
+    if (source != null) {
+      _result.source = source;
+    }
+    return _result;
+  }
   factory CompileDDCRequest.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -169,7 +189,19 @@
     ..hasRequiredFields = false;
 
   SourceRequest._() : super();
-  factory SourceRequest() => create();
+  factory SourceRequest({
+    $core.String source,
+    $core.int offset,
+  }) {
+    final _result = create();
+    if (source != null) {
+      _result.source = source;
+    }
+    if (offset != null) {
+      _result.offset = offset;
+    }
+    return _result;
+  }
   factory SourceRequest.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -243,7 +275,23 @@
     ..hasRequiredFields = false;
 
   AnalysisResults._() : super();
-  factory AnalysisResults() => create();
+  factory AnalysisResults({
+    $core.Iterable<AnalysisIssue> issues,
+    $core.Iterable<$core.String> packageImports,
+    ErrorMessage error,
+  }) {
+    final _result = create();
+    if (issues != null) {
+      _result.issues.addAll(issues);
+    }
+    if (packageImports != null) {
+      _result.packageImports.addAll(packageImports);
+    }
+    if (error != null) {
+      _result.error = error;
+    }
+    return _result;
+  }
   factory AnalysisResults.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -325,7 +373,39 @@
     ..hasRequiredFields = false;
 
   AnalysisIssue._() : super();
-  factory AnalysisIssue() => create();
+  factory AnalysisIssue({
+    $core.String kind,
+    $core.int line,
+    $core.String message,
+    $core.String sourceName,
+    $core.bool hasFixes,
+    $core.int charStart,
+    $core.int charLength,
+  }) {
+    final _result = create();
+    if (kind != null) {
+      _result.kind = kind;
+    }
+    if (line != null) {
+      _result.line = line;
+    }
+    if (message != null) {
+      _result.message = message;
+    }
+    if (sourceName != null) {
+      _result.sourceName = sourceName;
+    }
+    if (hasFixes != null) {
+      _result.hasFixes = hasFixes;
+    }
+    if (charStart != null) {
+      _result.charStart = charStart;
+    }
+    if (charLength != null) {
+      _result.charLength = charLength;
+    }
+    return _result;
+  }
   factory AnalysisIssue.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -506,7 +586,23 @@
     ..hasRequiredFields = false;
 
   CompileResponse._() : super();
-  factory CompileResponse() => create();
+  factory CompileResponse({
+    $core.String result,
+    $core.String sourceMap,
+    ErrorMessage error,
+  }) {
+    final _result = create();
+    if (result != null) {
+      _result.result = result;
+    }
+    if (sourceMap != null) {
+      _result.sourceMap = sourceMap;
+    }
+    if (error != null) {
+      _result.error = error;
+    }
+    return _result;
+  }
   factory CompileResponse.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -599,7 +695,23 @@
     ..hasRequiredFields = false;
 
   CompileDDCResponse._() : super();
-  factory CompileDDCResponse() => create();
+  factory CompileDDCResponse({
+    $core.String result,
+    $core.String modulesBaseUrl,
+    ErrorMessage error,
+  }) {
+    final _result = create();
+    if (result != null) {
+      _result.result = result;
+    }
+    if (modulesBaseUrl != null) {
+      _result.modulesBaseUrl = modulesBaseUrl;
+    }
+    if (error != null) {
+      _result.error = error;
+    }
+    return _result;
+  }
   factory CompileDDCResponse.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -691,7 +803,19 @@
     ..hasRequiredFields = false;
 
   DocumentResponse._() : super();
-  factory DocumentResponse() => create();
+  factory DocumentResponse({
+    $core.Map<$core.String, $core.String> info,
+    ErrorMessage error,
+  }) {
+    final _result = create();
+    if (info != null) {
+      _result.info.addAll(info);
+    }
+    if (error != null) {
+      _result.error = error;
+    }
+    return _result;
+  }
   factory DocumentResponse.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -760,7 +884,27 @@
     ..hasRequiredFields = false;
 
   CompleteResponse._() : super();
-  factory CompleteResponse() => create();
+  factory CompleteResponse({
+    $core.int replacementOffset,
+    $core.int replacementLength,
+    $core.Iterable<Completion> completions,
+    ErrorMessage error,
+  }) {
+    final _result = create();
+    if (replacementOffset != null) {
+      _result.replacementOffset = replacementOffset;
+    }
+    if (replacementLength != null) {
+      _result.replacementLength = replacementLength;
+    }
+    if (completions != null) {
+      _result.completions.addAll(completions);
+    }
+    if (error != null) {
+      _result.error = error;
+    }
+    return _result;
+  }
   factory CompleteResponse.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -852,7 +996,15 @@
     ..hasRequiredFields = false;
 
   Completion._() : super();
-  factory Completion() => create();
+  factory Completion({
+    $core.Map<$core.String, $core.String> completion,
+  }) {
+    final _result = create();
+    if (completion != null) {
+      _result.completion.addAll(completion);
+    }
+    return _result;
+  }
   factory Completion.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -906,7 +1058,19 @@
     ..hasRequiredFields = false;
 
   FixesResponse._() : super();
-  factory FixesResponse() => create();
+  factory FixesResponse({
+    $core.Iterable<ProblemAndFixes> fixes,
+    ErrorMessage error,
+  }) {
+    final _result = create();
+    if (fixes != null) {
+      _result.fixes.addAll(fixes);
+    }
+    if (error != null) {
+      _result.error = error;
+    }
+    return _result;
+  }
   factory FixesResponse.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -976,7 +1140,27 @@
     ..hasRequiredFields = false;
 
   ProblemAndFixes._() : super();
-  factory ProblemAndFixes() => create();
+  factory ProblemAndFixes({
+    $core.Iterable<CandidateFix> fixes,
+    $core.String problemMessage,
+    $core.int offset,
+    $core.int length,
+  }) {
+    final _result = create();
+    if (fixes != null) {
+      _result.fixes.addAll(fixes);
+    }
+    if (problemMessage != null) {
+      _result.problemMessage = problemMessage;
+    }
+    if (offset != null) {
+      _result.offset = offset;
+    }
+    if (length != null) {
+      _result.length = length;
+    }
+    return _result;
+  }
   factory ProblemAndFixes.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -1071,7 +1255,27 @@
     ..hasRequiredFields = false;
 
   CandidateFix._() : super();
-  factory CandidateFix() => create();
+  factory CandidateFix({
+    $core.String message,
+    $core.Iterable<SourceEdit> edits,
+    $core.int selectionOffset,
+    $core.Iterable<LinkedEditGroup> linkedEditGroups,
+  }) {
+    final _result = create();
+    if (message != null) {
+      _result.message = message;
+    }
+    if (edits != null) {
+      _result.edits.addAll(edits);
+    }
+    if (selectionOffset != null) {
+      _result.selectionOffset = selectionOffset;
+    }
+    if (linkedEditGroups != null) {
+      _result.linkedEditGroups.addAll(linkedEditGroups);
+    }
+    return _result;
+  }
   factory CandidateFix.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -1157,7 +1361,23 @@
     ..hasRequiredFields = false;
 
   SourceEdit._() : super();
-  factory SourceEdit() => create();
+  factory SourceEdit({
+    $core.int offset,
+    $core.int length,
+    $core.String replacement,
+  }) {
+    final _result = create();
+    if (offset != null) {
+      _result.offset = offset;
+    }
+    if (length != null) {
+      _result.length = length;
+    }
+    if (replacement != null) {
+      _result.replacement = replacement;
+    }
+    return _result;
+  }
   factory SourceEdit.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -1250,7 +1470,23 @@
     ..hasRequiredFields = false;
 
   LinkedEditGroup._() : super();
-  factory LinkedEditGroup() => create();
+  factory LinkedEditGroup({
+    $core.Iterable<$core.int> positions,
+    $core.int length,
+    $core.Iterable<LinkedEditSuggestion> suggestions,
+  }) {
+    final _result = create();
+    if (positions != null) {
+      _result.positions.addAll(positions);
+    }
+    if (length != null) {
+      _result.length = length;
+    }
+    if (suggestions != null) {
+      _result.suggestions.addAll(suggestions);
+    }
+    return _result;
+  }
   factory LinkedEditGroup.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -1320,7 +1556,19 @@
     ..hasRequiredFields = false;
 
   LinkedEditSuggestion._() : super();
-  factory LinkedEditSuggestion() => create();
+  factory LinkedEditSuggestion({
+    $core.String value,
+    $core.String kind,
+  }) {
+    final _result = create();
+    if (value != null) {
+      _result.value = value;
+    }
+    if (kind != null) {
+      _result.kind = kind;
+    }
+    return _result;
+  }
   factory LinkedEditSuggestion.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -1399,7 +1647,23 @@
     ..hasRequiredFields = false;
 
   FormatResponse._() : super();
-  factory FormatResponse() => create();
+  factory FormatResponse({
+    $core.String newString,
+    $core.int offset,
+    ErrorMessage error,
+  }) {
+    final _result = create();
+    if (newString != null) {
+      _result.newString = newString;
+    }
+    if (offset != null) {
+      _result.offset = offset;
+    }
+    if (error != null) {
+      _result.error = error;
+    }
+    return _result;
+  }
   factory FormatResponse.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -1489,7 +1753,19 @@
     ..hasRequiredFields = false;
 
   AssistsResponse._() : super();
-  factory AssistsResponse() => create();
+  factory AssistsResponse({
+    $core.Iterable<CandidateFix> assists,
+    ErrorMessage error,
+  }) {
+    final _result = create();
+    if (assists != null) {
+      _result.assists.addAll(assists);
+    }
+    if (error != null) {
+      _result.error = error;
+    }
+    return _result;
+  }
   factory AssistsResponse.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -1561,7 +1837,47 @@
     ..hasRequiredFields = false;
 
   VersionResponse._() : super();
-  factory VersionResponse() => create();
+  factory VersionResponse({
+    $core.String sdkVersion,
+    $core.String sdkVersionFull,
+    $core.String runtimeVersion,
+    $core.String appEngineVersion,
+    $core.String servicesVersion,
+    $core.String flutterVersion,
+    $core.String flutterDartVersion,
+    $core.String flutterDartVersionFull,
+    ErrorMessage error,
+  }) {
+    final _result = create();
+    if (sdkVersion != null) {
+      _result.sdkVersion = sdkVersion;
+    }
+    if (sdkVersionFull != null) {
+      _result.sdkVersionFull = sdkVersionFull;
+    }
+    if (runtimeVersion != null) {
+      _result.runtimeVersion = runtimeVersion;
+    }
+    if (appEngineVersion != null) {
+      _result.appEngineVersion = appEngineVersion;
+    }
+    if (servicesVersion != null) {
+      _result.servicesVersion = servicesVersion;
+    }
+    if (flutterVersion != null) {
+      _result.flutterVersion = flutterVersion;
+    }
+    if (flutterDartVersion != null) {
+      _result.flutterDartVersion = flutterDartVersion;
+    }
+    if (flutterDartVersionFull != null) {
+      _result.flutterDartVersionFull = flutterDartVersionFull;
+    }
+    if (error != null) {
+      _result.error = error;
+    }
+    return _result;
+  }
   factory VersionResponse.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -1719,7 +2035,15 @@
     ..hasRequiredFields = false;
 
   BadRequest._() : super();
-  factory BadRequest() => create();
+  factory BadRequest({
+    ErrorMessage error,
+  }) {
+    final _result = create();
+    if (error != null) {
+      _result.error = error;
+    }
+    return _result;
+  }
   factory BadRequest.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
@@ -1779,7 +2103,15 @@
     ..hasRequiredFields = false;
 
   ErrorMessage._() : super();
-  factory ErrorMessage() => create();
+  factory ErrorMessage({
+    $core.String message,
+  }) {
+    final _result = create();
+    if (message != null) {
+      _result.message = message;
+    }
+    return _result;
+  }
   factory ErrorMessage.fromBuffer($core.List<$core.int> i,
           [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
       create()..mergeFromBuffer(i, r);
diff --git a/lib/src/protos/dart_services.pbenum.dart b/lib/src/protos/dart_services.pbenum.dart
index ad2ace1..9598e3e 100644
--- a/lib/src/protos/dart_services.pbenum.dart
+++ b/lib/src/protos/dart_services.pbenum.dart
@@ -2,5 +2,5 @@
 //  Generated code. Do not modify.
 //  source: protos/dart_services.proto
 //
-// @dart = 2.3
+// @dart = 2.7
 // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
diff --git a/lib/src/protos/dart_services.pbjson.dart b/lib/src/protos/dart_services.pbjson.dart
index 1a9d571..b85ece2 100644
--- a/lib/src/protos/dart_services.pbjson.dart
+++ b/lib/src/protos/dart_services.pbjson.dart
@@ -2,7 +2,7 @@
 //  Generated code. Do not modify.
 //  source: protos/dart_services.proto
 //
-// @dart = 2.3
+// @dart = 2.7
 // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
 
 const CompileRequest$json = {
diff --git a/lib/src/protos/dart_services.pbserver.dart b/lib/src/protos/dart_services.pbserver.dart
index a31ab16..d9e5442 100644
--- a/lib/src/protos/dart_services.pbserver.dart
+++ b/lib/src/protos/dart_services.pbserver.dart
@@ -2,7 +2,7 @@
 //  Generated code. Do not modify.
 //  source: protos/dart_services.proto
 //
-// @dart = 2.3
+// @dart = 2.7
 // ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
 
 export 'dart_services.pb.dart';
diff --git a/lib/src/sdk_manager.dart b/lib/src/sdk_manager.dart
index 5ea8fa6..af9a071 100644
--- a/lib/src/sdk_manager.dart
+++ b/lib/src/sdk_manager.dart
@@ -64,10 +64,12 @@
   String get sdkPath => path.dirname(path.dirname(Platform.resolvedExecutable));
 }
 
+/// The Flutter SDK is asuumed to be available at `./flutter-sdk/`.
+final flutterSdkPath =
+    Directory(path.join(Directory.current.path, 'flutter-sdk'));
+
 /// Represents a Flutter SDK installation (which includes its own version of the
 /// Dart SDK) present on the server.
-///
-/// This class assumes the Flutter SDK is available at `./flutter`.
 class FlutterSdk extends Sdk {
   String _versionFull = '';
   String _flutterVersion = '';
@@ -77,16 +79,14 @@
     _versionFull =
         (await File(path.join(sdkPath, 'version')).readAsString()).trim();
     _flutterVersion =
-        (await File(path.join(Directory.current.path, 'flutter', 'version'))
-                .readAsString())
+        (await File(path.join(flutterSdkPath.path, 'version')).readAsString())
             .trim();
   }
 
   @override
   String get sdkPath => path.join(flutterBinPath, 'cache', 'dart-sdk');
 
-  String get flutterBinPath =>
-      path.join(Directory.current.path, 'flutter', 'bin');
+  String get flutterBinPath => path.join(flutterSdkPath.path, 'bin');
 
   @override
   String get versionFull => _versionFull;
diff --git a/pubspec.lock b/pubspec.lock
index a6cee27..cec5131 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -36,6 +36,13 @@
       url: "https://pub.dartlang.org"
     source: hosted
     version: "0.12.0"
+  archive:
+    dependency: transitive
+    description:
+      name: archive
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "2.0.13"
   args:
     dependency: "direct main"
     description:
@@ -49,7 +56,7 @@
       name: async
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.4.2"
+    version: "2.5.0"
   bazel_worker:
     dependency: "direct main"
     description:
@@ -63,14 +70,14 @@
       name: boolean_selector
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.0.0"
+    version: "2.1.0"
   build:
     dependency: transitive
     description:
       name: build
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.6.0"
+    version: "1.6.2"
   build_config:
     dependency: transitive
     description:
@@ -84,28 +91,28 @@
       name: build_daemon
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.1.4"
+    version: "2.1.7"
   build_resolvers:
     dependency: transitive
     description:
       name: build_resolvers
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.5.1"
+    version: "1.5.3"
   build_runner:
     dependency: "direct dev"
     description:
       name: build_runner
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.10.11"
+    version: "1.11.1"
   build_runner_core:
     dependency: transitive
     description:
       name: build_runner_core
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "6.1.5"
+    version: "6.1.7"
   built_collection:
     dependency: transitive
     description:
@@ -126,7 +133,7 @@
       name: charcode
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.3"
+    version: "1.2.0"
   checked_yaml:
     dependency: transitive
     description:
@@ -141,27 +148,34 @@
       url: "https://pub.dartlang.org"
     source: hosted
     version: "0.2.0"
+  clock:
+    dependency: transitive
+    description:
+      name: clock
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.1.0"
   code_builder:
     dependency: transitive
     description:
       name: code_builder
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "3.5.0"
+    version: "3.6.0"
   codemirror:
     dependency: "direct dev"
     description:
       name: codemirror
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.5.23+5.59.1"
+    version: "0.5.24+5.59.2"
   collection:
     dependency: transitive
     description:
       name: collection
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.14.13"
+    version: "1.15.0"
   convert:
     dependency: transitive
     description:
@@ -238,7 +252,7 @@
       name: googleapis_auth
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.2.12"
+    version: "0.2.12+1"
   graphs:
     dependency: transitive
     description:
@@ -259,7 +273,7 @@
       name: grpc
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.8.0"
+    version: "2.9.0"
   http:
     dependency: "direct main"
     description:
@@ -308,7 +322,7 @@
       name: intl
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.16.1"
+    version: "0.17.0"
   io:
     dependency: transitive
     description:
@@ -322,7 +336,7 @@
       name: js
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.6.2"
+    version: "0.6.3"
   json_annotation:
     dependency: transitive
     description:
@@ -350,7 +364,7 @@
       name: meta
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.2.4"
+    version: "1.3.0"
   mime:
     dependency: transitive
     description:
@@ -385,7 +399,7 @@
       name: node_preamble
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.4.12"
+    version: "1.4.13"
   package_config:
     dependency: transitive
     description:
@@ -399,7 +413,7 @@
       name: path
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.7.0"
+    version: "1.8.0"
   pedantic:
     dependency: "direct dev"
     description:
@@ -413,7 +427,7 @@
       name: pool
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.4.0"
+    version: "1.5.0"
   protobuf:
     dependency: "direct main"
     description:
@@ -455,7 +469,7 @@
       name: shelf_packages_handler
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.0.0"
+    version: "2.0.1"
   shelf_router:
     dependency: "direct main"
     description:
@@ -469,21 +483,21 @@
       name: shelf_router_generator
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.7.2+3"
+    version: "0.7.2+4"
   shelf_static:
     dependency: transitive
     description:
       name: shelf_static
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.2.9+1"
+    version: "0.2.9+2"
   shelf_web_socket:
     dependency: transitive
     description:
       name: shelf_web_socket
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.2.3"
+    version: "0.2.4"
   source_gen:
     dependency: transitive
     description:
@@ -497,35 +511,35 @@
       name: source_map_stack_trace
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.0.0"
+    version: "2.1.0"
   source_maps:
     dependency: transitive
     description:
       name: source_maps
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.10.9"
+    version: "0.10.10"
   source_span:
     dependency: transitive
     description:
       name: source_span
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.7.0"
+    version: "1.8.0"
   stack_trace:
     dependency: transitive
     description:
       name: stack_trace
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.9.6"
+    version: "1.10.0"
   stream_channel:
     dependency: transitive
     description:
       name: stream_channel
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.0.0"
+    version: "2.1.0"
   stream_transform:
     dependency: transitive
     description:
@@ -539,7 +553,7 @@
       name: string_scanner
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.0.5"
+    version: "1.1.0"
   synchronized:
     dependency: "direct dev"
     description:
@@ -553,7 +567,7 @@
       name: term_glyph
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.0"
+    version: "1.2.0"
   test:
     dependency: "direct dev"
     description:
@@ -588,7 +602,7 @@
       name: typed_data
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.2.0"
+    version: "1.3.0"
   vm_service:
     dependency: transitive
     description:
@@ -609,7 +623,7 @@
       name: web_socket_channel
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.0"
+    version: "1.2.0"
   webkit_inspection_protocol:
     dependency: transitive
     description:
@@ -625,4 +639,4 @@
     source: hosted
     version: "2.2.1"
 sdks:
-  dart: ">=2.10.4 <3.0.0"
+  dart: ">=2.12.0-0.0 <3.0.0"
diff --git a/tool/grind.dart b/tool/grind.dart
index 0beb8de..f51df71 100644
--- a/tool/grind.dart
+++ b/tool/grind.dart
@@ -156,14 +156,12 @@
 }
 
 Future _buildStorageArtifacts(Directory dir) async {
-  final flutterSdkPath =
-      Directory(path.join(Directory.current.path, 'flutter'));
   final pubspec = createPubspec(includeFlutterWeb: true);
   joinFile(dir, ['pubspec.yaml']).writeAsStringSync(pubspec);
 
   // run flutter pub get
   await runWithLogging(
-    path.join(flutterSdkPath.path, 'bin/flutter'),
+    path.join(flutterSdkPath.path, 'bin', 'flutter'),
     arguments: ['pub', 'get'],
     workingDirectory: dir.path,
   );
@@ -196,7 +194,7 @@
     }
   }
 
-  // Make sure flutter/bin/cache/flutter_web_sdk/flutter_web_sdk/kernel/flutter_ddc_sdk.dill
+  // Make sure flutter-sdk/bin/cache/flutter_web_sdk/flutter_web_sdk/kernel/flutter_ddc_sdk.dill
   // is installed.
   await runWithLogging(
     path.join(flutterSdkPath.path, 'bin', 'flutter'),
@@ -207,10 +205,10 @@
   // Build the artifacts using DDC:
   // dart-sdk/bin/dartdevc -s kernel/flutter_ddc_sdk.dill
   //     --modules=amd package:flutter/animation.dart ...
-  final compilerPath =
-      path.join(flutterSdkPath.path, 'bin/cache/dart-sdk/bin/dartdevc');
-  final dillPath = path.join(flutterSdkPath.path,
-      'bin/cache/flutter_web_sdk/flutter_web_sdk/kernel/flutter_ddc_sdk.dill');
+  final compilerPath = path.join(
+      flutterSdkPath.path, 'bin', 'cache', 'dart-sdk', 'bin', 'dartdevc');
+  final dillPath = path.join(flutterSdkPath.path, 'bin', 'cache',
+      'flutter_web_sdk', 'flutter_web_sdk', 'kernel', 'flutter_ddc_sdk.dill');
 
   final args = <String>[
     '-s',
@@ -246,33 +244,30 @@
 }
 
 @Task('Delete, re-download, and reinitialize the Flutter submodule.')
-void setupFlutterSubmodule() async {
-  final flutterDir = Directory('flutter');
+void setupFlutterSdk() async {
+  await flutterSdkPath.delete(recursive: true);
 
-  // Remove all files currently in the submodule. This is done to clear any
-  // internal state the Flutter/Dart SDKs may have created on their own.
-  flutterDir.listSync().forEach((e) => e.deleteSync(recursive: true));
+  final info = DownloadingSdkManager.getSdkConfigInfo();
+  print('Flutter SDK configuration: $info\n');
 
-  // Pull clean files into the submodule, based on whatever commit it's set to.
+  // Download the SDK into ./futter-sdk/
+  await DownloadingSdkManager().createFromConfigFile();
+
+  // Set up the  Flutter SDK the way dart-services needs it.
+
+  final flutterBinFlutter = path.join(flutterSdkPath.path, 'bin', 'flutter');
   await runWithLogging(
-    'git',
-    arguments: ['submodule', 'update'],
-  );
-
-  // Set up the submodule's copy of the Flutter SDK the way dart-services needs
-  // it.
-  await runWithLogging(
-    path.join(flutterDir.path, 'bin/flutter'),
+    flutterBinFlutter,
     arguments: ['doctor'],
   );
 
   await runWithLogging(
-    path.join(flutterDir.path, 'bin/flutter'),
+    flutterBinFlutter,
     arguments: ['config', '--enable-web'],
   );
 
   await runWithLogging(
-    path.join(flutterDir.path, 'bin/flutter'),
+    flutterBinFlutter,
     arguments: [
       'precache',
       '--web',
@@ -292,8 +287,8 @@
 }
 
 @Task('Update generated files and run all checks prior to deployment')
-@Depends(setupFlutterSubmodule, updateDockerVersion, generateProtos, analyze,
-    test, fuzz, validateStorageArtifacts)
+@Depends(setupFlutterSdk, updateDockerVersion, generateProtos, analyze, test,
+    fuzz, validateStorageArtifacts)
 void deploy() {
   log('Run: gcloud app deploy --project=dart-services --no-promote');
 }