Add FlMethodChannel, FlMethodCodec, FlStandardMethodCodec and FlJsonMethodCodec (#18220)

* Add FlMethodChannel, FlMethodCall, FlMethodResponse and FlMethodCodec

* Add FlJsonMethodCodec

* Add FlStandardMethodCodec
diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter
index 7ed3bbe..ef5efd6 100755
--- a/ci/licenses_golden/licenses_flutter
+++ b/ci/licenses_golden/licenses_flutter
@@ -1200,8 +1200,19 @@
 FILE: ../../../flutter/shell/platform/linux/fl_engine_private.h
 FILE: ../../../flutter/shell/platform/linux/fl_json_message_codec.cc
 FILE: ../../../flutter/shell/platform/linux/fl_json_message_codec_test.cc
+FILE: ../../../flutter/shell/platform/linux/fl_json_method_codec.cc
+FILE: ../../../flutter/shell/platform/linux/fl_json_method_codec_test.cc
 FILE: ../../../flutter/shell/platform/linux/fl_message_codec.cc
 FILE: ../../../flutter/shell/platform/linux/fl_message_codec_test.cc
+FILE: ../../../flutter/shell/platform/linux/fl_method_call.cc
+FILE: ../../../flutter/shell/platform/linux/fl_method_call_private.h
+FILE: ../../../flutter/shell/platform/linux/fl_method_channel.cc
+FILE: ../../../flutter/shell/platform/linux/fl_method_channel_private.h
+FILE: ../../../flutter/shell/platform/linux/fl_method_codec.cc
+FILE: ../../../flutter/shell/platform/linux/fl_method_codec_private.h
+FILE: ../../../flutter/shell/platform/linux/fl_method_codec_test.cc
+FILE: ../../../flutter/shell/platform/linux/fl_method_response.cc
+FILE: ../../../flutter/shell/platform/linux/fl_method_response_test.cc
 FILE: ../../../flutter/shell/platform/linux/fl_renderer.cc
 FILE: ../../../flutter/shell/platform/linux/fl_renderer.h
 FILE: ../../../flutter/shell/platform/linux/fl_renderer_x11.cc
@@ -1209,6 +1220,8 @@
 FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec.cc
 FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_private.h
 FILE: ../../../flutter/shell/platform/linux/fl_standard_message_codec_test.cc
+FILE: ../../../flutter/shell/platform/linux/fl_standard_method_codec.cc
+FILE: ../../../flutter/shell/platform/linux/fl_standard_method_codec_test.cc
 FILE: ../../../flutter/shell/platform/linux/fl_string_codec.cc
 FILE: ../../../flutter/shell/platform/linux/fl_string_codec_test.cc
 FILE: ../../../flutter/shell/platform/linux/fl_value.cc
@@ -1220,8 +1233,14 @@
 FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_dart_project.h
 FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_engine.h
 FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h
+FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h
 FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h
+FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_call.h
+FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h
+FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h
+FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_method_response.h
 FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h
+FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h
 FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_string_codec.h
 FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_value.h
 FILE: ../../../flutter/shell/platform/linux/public/flutter_linux/fl_view.h
diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn
index aa2c35a..d32c572 100644
--- a/shell/platform/linux/BUILD.gn
+++ b/shell/platform/linux/BUILD.gn
@@ -50,8 +50,14 @@
   "public/flutter_linux/fl_dart_project.h",
   "public/flutter_linux/fl_engine.h",
   "public/flutter_linux/fl_json_message_codec.h",
+  "public/flutter_linux/fl_json_method_codec.h",
   "public/flutter_linux/fl_message_codec.h",
+  "public/flutter_linux/fl_method_call.h",
+  "public/flutter_linux/fl_method_channel.h",
+  "public/flutter_linux/fl_method_codec.h",
+  "public/flutter_linux/fl_method_response.h",
   "public/flutter_linux/fl_standard_message_codec.h",
+  "public/flutter_linux/fl_standard_method_codec.h",
   "public/flutter_linux/fl_string_codec.h",
   "public/flutter_linux/fl_value.h",
   "public/flutter_linux/fl_view.h",
@@ -72,10 +78,16 @@
     "fl_dart_project.cc",
     "fl_engine.cc",
     "fl_json_message_codec.cc",
+    "fl_json_method_codec.cc",
     "fl_message_codec.cc",
+    "fl_method_call.cc",
+    "fl_method_channel.cc",
+    "fl_method_codec.cc",
+    "fl_method_response.cc",
     "fl_renderer.cc",
     "fl_renderer_x11.cc",
     "fl_standard_message_codec.cc",
+    "fl_standard_method_codec.cc",
     "fl_string_codec.cc",
     "fl_value.cc",
     "fl_view.cc",
@@ -107,8 +119,12 @@
     "fl_binary_codec_test.cc",
     "fl_dart_project_test.cc",
     "fl_json_message_codec_test.cc",
+    "fl_json_method_codec_test.cc",
     "fl_message_codec_test.cc",
+    "fl_method_codec_test.cc",
+    "fl_method_response_test.cc",
     "fl_standard_message_codec_test.cc",
+    "fl_standard_method_codec_test.cc",
     "fl_string_codec_test.cc",
     "fl_value_test.cc",
     "testing/fl_test.cc",
diff --git a/shell/platform/linux/fl_json_method_codec.cc b/shell/platform/linux/fl_json_method_codec.cc
new file mode 100644
index 0000000..0a0cd73
--- /dev/null
+++ b/shell/platform/linux/fl_json_method_codec.cc
@@ -0,0 +1,205 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h"
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
+
+#include <gmodule.h>
+
+static constexpr char kMethodKey[] = "method";
+static constexpr char kArgsKey[] = "args";
+
+struct _FlJsonMethodCodec {
+  FlMethodCodec parent_instance;
+
+  FlJsonMessageCodec* codec;
+};
+
+G_DEFINE_TYPE(FlJsonMethodCodec,
+              fl_json_method_codec,
+              fl_method_codec_get_type())
+
+static void fl_json_method_codec_dispose(GObject* object) {
+  FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(object);
+
+  g_clear_object(&self->codec);
+
+  G_OBJECT_CLASS(fl_json_method_codec_parent_class)->dispose(object);
+}
+
+// Implements FlMethodCodec::encode_method_call
+static GBytes* fl_json_method_codec_encode_method_call(FlMethodCodec* codec,
+                                                       const gchar* name,
+                                                       FlValue* args,
+                                                       GError** error) {
+  FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec);
+
+  g_autoptr(FlValue) message = fl_value_new_map();
+  fl_value_set_take(message, fl_value_new_string(kMethodKey),
+                    fl_value_new_string(name));
+  fl_value_set_take(message, fl_value_new_string(kArgsKey),
+                    args != nullptr ? fl_value_ref(args) : fl_value_new_null());
+
+  return fl_message_codec_encode_message(FL_MESSAGE_CODEC(self->codec), message,
+                                         error);
+}
+
+// Implements FlMethodCodec::decode_method_call
+static gboolean fl_json_method_codec_decode_method_call(FlMethodCodec* codec,
+                                                        GBytes* message,
+                                                        gchar** name,
+                                                        FlValue** args,
+                                                        GError** error) {
+  FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec);
+
+  g_autoptr(FlValue) value = fl_message_codec_decode_message(
+      FL_MESSAGE_CODEC(self->codec), message, error);
+  if (value == nullptr)
+    return FALSE;
+
+  if (fl_value_get_type(value) != FL_VALUE_TYPE_MAP) {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                "Expected JSON map in method resonse, got %d instead",
+                fl_value_get_type(value));
+    return FALSE;
+  }
+
+  FlValue* method_value = fl_value_lookup_string(value, kMethodKey);
+  if (method_value == nullptr) {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                "Missing JSON method field in method resonse");
+    return FALSE;
+  }
+  if (fl_value_get_type(method_value) != FL_VALUE_TYPE_STRING) {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                "Expected JSON string for method name, got %d instead",
+                fl_value_get_type(method_value));
+    return FALSE;
+  }
+  FlValue* args_value = fl_value_lookup_string(value, kArgsKey);
+
+  *name = g_strdup(fl_value_get_string(method_value));
+  *args = args_value != nullptr ? fl_value_ref(args_value) : nullptr;
+
+  return TRUE;
+}
+
+// Implements FlMethodCodec::encode_success_envelope
+static GBytes* fl_json_method_codec_encode_success_envelope(
+    FlMethodCodec* codec,
+    FlValue* result,
+    GError** error) {
+  FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec);
+
+  g_autoptr(FlValue) message = fl_value_new_list();
+  fl_value_append_take(
+      message, result != nullptr ? fl_value_ref(result) : fl_value_new_null());
+
+  return fl_message_codec_encode_message(FL_MESSAGE_CODEC(self->codec), message,
+                                         error);
+}
+
+// Implements FlMethodCodec::encode_error_envelope
+static GBytes* fl_json_method_codec_encode_error_envelope(
+    FlMethodCodec* codec,
+    const gchar* code,
+    const gchar* error_message,
+    FlValue* details,
+    GError** error) {
+  FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec);
+
+  g_autoptr(FlValue) message = fl_value_new_list();
+  fl_value_append_take(message, fl_value_new_string(code));
+  fl_value_append_take(message, error_message != nullptr
+                                    ? fl_value_new_string(error_message)
+                                    : fl_value_new_null());
+  fl_value_append_take(message, details != nullptr ? fl_value_ref(details)
+                                                   : fl_value_new_null());
+
+  return fl_message_codec_encode_message(FL_MESSAGE_CODEC(self->codec), message,
+                                         error);
+}
+
+// Implements FlMethodCodec::decode_response
+static FlMethodResponse* fl_json_method_codec_decode_response(
+    FlMethodCodec* codec,
+    GBytes* message,
+    GError** error) {
+  FlJsonMethodCodec* self = FL_JSON_METHOD_CODEC(codec);
+
+  g_autoptr(FlValue) value = fl_message_codec_decode_message(
+      FL_MESSAGE_CODEC(self->codec), message, error);
+  if (value == nullptr)
+    return nullptr;
+
+  if (fl_value_get_type(value) != FL_VALUE_TYPE_LIST) {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                "Expected JSON list in method resonse, got %d instead",
+                fl_value_get_type(value));
+    return nullptr;
+  }
+
+  size_t length = fl_value_get_length(value);
+  if (length == 1) {
+    return FL_METHOD_RESPONSE(
+        fl_method_success_response_new(fl_value_get_list_value(value, 0)));
+  } else if (length == 3) {
+    FlValue* code_value = fl_value_get_list_value(value, 0);
+    if (fl_value_get_type(code_value) != FL_VALUE_TYPE_STRING) {
+      g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                  "Error code wrong type");
+      return nullptr;
+    }
+    const gchar* code = fl_value_get_string(code_value);
+
+    FlValue* message_value = fl_value_get_list_value(value, 1);
+    if (fl_value_get_type(message_value) != FL_VALUE_TYPE_STRING &&
+        fl_value_get_type(message_value) != FL_VALUE_TYPE_NULL) {
+      g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                  "Error message wrong type");
+      return nullptr;
+    }
+    const gchar* message =
+        fl_value_get_type(message_value) == FL_VALUE_TYPE_STRING
+            ? fl_value_get_string(message_value)
+            : nullptr;
+
+    FlValue* args = fl_value_get_list_value(value, 2);
+    if (fl_value_get_type(args) == FL_VALUE_TYPE_NULL)
+      args = nullptr;
+
+    return FL_METHOD_RESPONSE(
+        fl_method_error_response_new(code, message, args));
+  } else {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                "Got response envelope of length %zi, expected 1 (success) or "
+                "3 (error)",
+                length);
+    return nullptr;
+  }
+}
+
+static void fl_json_method_codec_class_init(FlJsonMethodCodecClass* klass) {
+  G_OBJECT_CLASS(klass)->dispose = fl_json_method_codec_dispose;
+  FL_METHOD_CODEC_CLASS(klass)->encode_method_call =
+      fl_json_method_codec_encode_method_call;
+  FL_METHOD_CODEC_CLASS(klass)->decode_method_call =
+      fl_json_method_codec_decode_method_call;
+  FL_METHOD_CODEC_CLASS(klass)->encode_success_envelope =
+      fl_json_method_codec_encode_success_envelope;
+  FL_METHOD_CODEC_CLASS(klass)->encode_error_envelope =
+      fl_json_method_codec_encode_error_envelope;
+  FL_METHOD_CODEC_CLASS(klass)->decode_response =
+      fl_json_method_codec_decode_response;
+}
+
+static void fl_json_method_codec_init(FlJsonMethodCodec* self) {
+  self->codec = fl_json_message_codec_new();
+}
+
+G_MODULE_EXPORT FlJsonMethodCodec* fl_json_method_codec_new() {
+  return static_cast<FlJsonMethodCodec*>(
+      g_object_new(fl_json_method_codec_get_type(), nullptr));
+}
diff --git a/shell/platform/linux/fl_json_method_codec_test.cc b/shell/platform/linux/fl_json_method_codec_test.cc
new file mode 100644
index 0000000..c994500
--- /dev/null
+++ b/shell/platform/linux/fl_json_method_codec_test.cc
@@ -0,0 +1,398 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h"
+#include "flutter/shell/platform/linux/fl_method_codec_private.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h"
+#include "gtest/gtest.h"
+
+// Converts a binary blob to a string.
+static gchar* message_to_text(GBytes* message) {
+  size_t data_length;
+  const gchar* data =
+      static_cast<const gchar*>(g_bytes_get_data(message, &data_length));
+  return g_strndup(data, data_length);
+}
+
+// Converts a string to a binary blob.
+static GBytes* text_to_message(const gchar* text) {
+  return g_bytes_new(text, strlen(text));
+}
+
+// Encodes a method call using JsonMethodCodec to a UTF-8 string.
+static gchar* encode_method_call(const gchar* name, FlValue* args) {
+  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
+      FL_METHOD_CODEC(codec), name, args, &error);
+  EXPECT_NE(message, nullptr);
+  EXPECT_EQ(error, nullptr);
+
+  return message_to_text(message);
+}
+
+// Encodes a success envelope response using JsonMethodCodec to a UTF-8 string.
+static gchar* encode_success_envelope(FlValue* result) {
+  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_success_envelope(
+      FL_METHOD_CODEC(codec), result, &error);
+  EXPECT_NE(message, nullptr);
+  EXPECT_EQ(error, nullptr);
+
+  return message_to_text(message);
+}
+
+// Encodes a error envelope response using JsonMethodCodec to a UTF8 string.
+static gchar* encode_error_envelope(const gchar* error_code,
+                                    const gchar* error_message,
+                                    FlValue* details) {
+  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope(
+      FL_METHOD_CODEC(codec), error_code, error_message, details, &error);
+  EXPECT_NE(message, nullptr);
+  EXPECT_EQ(error, nullptr);
+
+  return message_to_text(message);
+}
+
+// Decodes a method call using JsonMethodCodec with a UTF8 string.
+static void decode_method_call(const char* text, gchar** name, FlValue** args) {
+  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+  g_autoptr(GBytes) data = text_to_message(text);
+  g_autoptr(GError) error = nullptr;
+  gboolean result = fl_method_codec_decode_method_call(
+      FL_METHOD_CODEC(codec), data, name, args, &error);
+  EXPECT_TRUE(result);
+  EXPECT_EQ(error, nullptr);
+}
+
+// Decodes a method call using JsonMethodCodec. Expect the given error.
+static void decode_error_method_call(const char* text,
+                                     GQuark domain,
+                                     gint code) {
+  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+  g_autoptr(GBytes) data = text_to_message(text);
+  g_autoptr(GError) error = nullptr;
+  g_autofree gchar* name = nullptr;
+  g_autoptr(FlValue) args = nullptr;
+  gboolean result = fl_method_codec_decode_method_call(
+      FL_METHOD_CODEC(codec), data, &name, &args, &error);
+  EXPECT_FALSE(result);
+  EXPECT_EQ(name, nullptr);
+  EXPECT_EQ(args, nullptr);
+  EXPECT_TRUE(g_error_matches(error, domain, code));
+}
+
+// Decodes a response using JsonMethodCodec. Expect the response is a result.
+static void decode_response_with_success(const char* text, FlValue* result) {
+  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+  g_autoptr(GBytes) message = text_to_message(text);
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlMethodResponse) response =
+      fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+  ASSERT_NE(response, nullptr);
+  EXPECT_EQ(error, nullptr);
+  ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
+  EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result(
+                                 FL_METHOD_SUCCESS_RESPONSE(response)),
+                             result));
+}
+
+// Decodes a response using JsonMethodCodec. Expect the response contains the
+// given error.
+static void decode_response_with_error(const char* text,
+                                       const gchar* code,
+                                       const gchar* error_message,
+                                       FlValue* details) {
+  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+  g_autoptr(GBytes) message = text_to_message(text);
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlMethodResponse) response =
+      fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+  ASSERT_NE(response, nullptr);
+  EXPECT_EQ(error, nullptr);
+  ASSERT_TRUE(FL_IS_METHOD_ERROR_RESPONSE(response));
+  EXPECT_STREQ(
+      fl_method_error_response_get_code(FL_METHOD_ERROR_RESPONSE(response)),
+      code);
+  if (error_message == nullptr)
+    EXPECT_EQ(fl_method_error_response_get_message(
+                  FL_METHOD_ERROR_RESPONSE(response)),
+              nullptr);
+  else
+    EXPECT_STREQ(fl_method_error_response_get_message(
+                     FL_METHOD_ERROR_RESPONSE(response)),
+                 error_message);
+  if (details == nullptr)
+    EXPECT_EQ(fl_method_error_response_get_details(
+                  FL_METHOD_ERROR_RESPONSE(response)),
+              nullptr);
+  else
+    EXPECT_TRUE(fl_value_equal(fl_method_error_response_get_details(
+                                   FL_METHOD_ERROR_RESPONSE(response)),
+                               details));
+}
+
+// Decode a response using JsonMethodCodec. Expect the given error.
+static void decode_error_response(const char* text, GQuark domain, gint code) {
+  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+  g_autoptr(GBytes) message = text_to_message(text);
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlMethodResponse) response =
+      fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+  EXPECT_EQ(response, nullptr);
+  EXPECT_TRUE(g_error_matches(error, domain, code));
+}
+
+TEST(FlJsonMethodCodecTest, EncodeMethodCallNullptrArgs) {
+  g_autofree gchar* text = encode_method_call("hello", nullptr);
+  EXPECT_STREQ(text, "{\"method\":\"hello\",\"args\":null}");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeMethodCallNullArgs) {
+  g_autoptr(FlValue) value = fl_value_new_null();
+  g_autofree gchar* text = encode_method_call("hello", value);
+  EXPECT_STREQ(text, "{\"method\":\"hello\",\"args\":null}");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeMethodCallStringArgs) {
+  g_autoptr(FlValue) args = fl_value_new_string("world");
+  g_autofree gchar* text = encode_method_call("hello", args);
+  EXPECT_STREQ(text, "{\"method\":\"hello\",\"args\":\"world\"}");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeMethodCallListArgs) {
+  g_autoptr(FlValue) args = fl_value_new_list();
+  fl_value_append_take(args, fl_value_new_string("count"));
+  fl_value_append_take(args, fl_value_new_int(42));
+  g_autofree gchar* text = encode_method_call("hello", args);
+  EXPECT_STREQ(text, "{\"method\":\"hello\",\"args\":[\"count\",42]}");
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallNoArgs) {
+  g_autofree gchar* name = nullptr;
+  g_autoptr(FlValue) args = nullptr;
+  decode_method_call("{\"method\":\"hello\"}", &name, &args);
+  EXPECT_STREQ(name, "hello");
+  ASSERT_EQ(args, nullptr);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallNullArgs) {
+  g_autofree gchar* name = nullptr;
+  g_autoptr(FlValue) args = nullptr;
+  decode_method_call("{\"method\":\"hello\",\"args\":null}", &name, &args);
+  EXPECT_STREQ(name, "hello");
+  ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_NULL);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallStringArgs) {
+  g_autofree gchar* name = nullptr;
+  g_autoptr(FlValue) args = nullptr;
+  decode_method_call("{\"method\":\"hello\",\"args\":\"world\"}", &name, &args);
+  EXPECT_STREQ(name, "hello");
+  ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_STRING);
+  EXPECT_STREQ(fl_value_get_string(args), "world");
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallListArgs) {
+  g_autofree gchar* name = nullptr;
+  g_autoptr(FlValue) args = nullptr;
+  decode_method_call("{\"method\":\"hello\",\"args\":[\"count\",42]}", &name,
+                     &args);
+  EXPECT_STREQ(name, "hello");
+  ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_LIST);
+  EXPECT_EQ(fl_value_get_length(args), static_cast<size_t>(2));
+
+  FlValue* arg0 = fl_value_get_list_value(args, 0);
+  ASSERT_EQ(fl_value_get_type(arg0), FL_VALUE_TYPE_STRING);
+  EXPECT_STREQ(fl_value_get_string(arg0), "count");
+
+  FlValue* arg1 = fl_value_get_list_value(args, 1);
+  ASSERT_EQ(fl_value_get_type(arg1), FL_VALUE_TYPE_INT);
+  EXPECT_EQ(fl_value_get_int(arg1), 42);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallNoData) {
+  decode_error_method_call("", FL_JSON_MESSAGE_CODEC_ERROR,
+                           FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallNoMethodOrArgs) {
+  decode_error_method_call("{}", FL_MESSAGE_CODEC_ERROR,
+                           FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallInvalidJson) {
+  decode_error_method_call("X", FL_JSON_MESSAGE_CODEC_ERROR,
+                           FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallWrongType) {
+  decode_error_method_call("42", FL_MESSAGE_CODEC_ERROR,
+                           FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallNoMethod) {
+  decode_error_method_call("{\"args\":\"world\"}", FL_MESSAGE_CODEC_ERROR,
+                           FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallNoTerminator) {
+  decode_error_method_call("{\"method\":\"hello\",\"args\":\"world\"",
+                           FL_JSON_MESSAGE_CODEC_ERROR,
+                           FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeMethodCallExtraData) {
+  decode_error_method_call("{\"method\":\"hello\"}XXX",
+                           FL_JSON_MESSAGE_CODEC_ERROR,
+                           FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
+
+TEST(FlJsonMethodCodecTest, EncodeSuccessEnvelopeNullptr) {
+  g_autofree gchar* text = encode_success_envelope(nullptr);
+  EXPECT_STREQ(text, "[null]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeSuccessEnvelopeNull) {
+  g_autoptr(FlValue) result = fl_value_new_null();
+  g_autofree gchar* text = encode_success_envelope(result);
+  EXPECT_STREQ(text, "[null]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeSuccessEnvelopeString) {
+  g_autoptr(FlValue) result = fl_value_new_string("hello");
+  g_autofree gchar* text = encode_success_envelope(result);
+  EXPECT_STREQ(text, "[\"hello\"]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeSuccessEnvelopeList) {
+  g_autoptr(FlValue) result = fl_value_new_list();
+  fl_value_append_take(result, fl_value_new_string("count"));
+  fl_value_append_take(result, fl_value_new_int(42));
+  g_autofree gchar* text = encode_success_envelope(result);
+  EXPECT_STREQ(text, "[[\"count\",42]]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeErrorEnvelopeEmptyCode) {
+  g_autofree gchar* text = encode_error_envelope("", nullptr, nullptr);
+  EXPECT_STREQ(text, "[\"\",null,null]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeErrorEnvelopeNonMessageOrDetails) {
+  g_autofree gchar* text = encode_error_envelope("error", nullptr, nullptr);
+  EXPECT_STREQ(text, "[\"error\",null,null]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeErrorEnvelopeMessage) {
+  g_autofree gchar* text = encode_error_envelope("error", "message", nullptr);
+  EXPECT_STREQ(text, "[\"error\",\"message\",null]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeErrorEnvelopeDetails) {
+  g_autoptr(FlValue) details = fl_value_new_list();
+  fl_value_append_take(details, fl_value_new_string("count"));
+  fl_value_append_take(details, fl_value_new_int(42));
+  g_autofree gchar* text = encode_error_envelope("error", nullptr, details);
+  EXPECT_STREQ(text, "[\"error\",null,[\"count\",42]]");
+}
+
+TEST(FlJsonMethodCodecTest, EncodeErrorEnvelopeMessageAndDetails) {
+  g_autoptr(FlValue) details = fl_value_new_list();
+  fl_value_append_take(details, fl_value_new_string("count"));
+  fl_value_append_take(details, fl_value_new_int(42));
+  g_autofree gchar* text = encode_error_envelope("error", "message", details);
+  EXPECT_STREQ(text, "[\"error\",\"message\",[\"count\",42]]");
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseSuccessNull) {
+  g_autoptr(FlValue) result = fl_value_new_null();
+  decode_response_with_success("[null]", result);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseSuccessString) {
+  g_autoptr(FlValue) result = fl_value_new_string("hello");
+  decode_response_with_success("[\"hello\"]", result);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseSuccessList) {
+  g_autoptr(FlValue) result = fl_value_new_list();
+  fl_value_append_take(result, fl_value_new_string("count"));
+  fl_value_append_take(result, fl_value_new_int(42));
+  decode_response_with_success("[[\"count\",42]]", result);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseErrorEmptyCode) {
+  decode_response_with_error("[\"\",null,null]", "", nullptr, nullptr);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseErrorNoMessageOrDetails) {
+  decode_response_with_error("[\"error\",null,null]", "error", nullptr,
+                             nullptr);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseErrorMessage) {
+  decode_response_with_error("[\"error\",\"message\",null]", "error", "message",
+                             nullptr);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseErrorDetails) {
+  g_autoptr(FlValue) details = fl_value_new_list();
+  fl_value_append_take(details, fl_value_new_string("count"));
+  fl_value_append_take(details, fl_value_new_int(42));
+  decode_response_with_error("[\"error\",null,[\"count\",42]]", "error",
+                             nullptr, details);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseErrorMessageAndDetails) {
+  g_autoptr(FlValue) details = fl_value_new_list();
+  fl_value_append_take(details, fl_value_new_string("count"));
+  fl_value_append_take(details, fl_value_new_int(42));
+  decode_response_with_error("[\"error\",\"message\",[\"count\",42]]", "error",
+                             "message", details);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseNotImplemented) {
+  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
+  g_autoptr(GBytes) message = g_bytes_new(nullptr, 0);
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlMethodResponse) response =
+      fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+  ASSERT_NE(response, nullptr);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_TRUE(FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response));
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseNoTerminator) {
+  decode_error_response("[42", FL_JSON_MESSAGE_CODEC_ERROR,
+                        FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseInvalidJson) {
+  decode_error_response("X", FL_JSON_MESSAGE_CODEC_ERROR,
+                        FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseMissingDetails) {
+  decode_error_response("[\"error\",\"message\"]", FL_MESSAGE_CODEC_ERROR,
+                        FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseExtraDetails) {
+  decode_error_response("[\"error\",\"message\",true,42]",
+                        FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseSuccessExtraData) {
+  decode_error_response("[null]X", FL_JSON_MESSAGE_CODEC_ERROR,
+                        FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
+
+TEST(FlJsonMethodCodecTest, DecodeResponseErrorExtraData) {
+  decode_error_response("[\"error\",null,null]X", FL_JSON_MESSAGE_CODEC_ERROR,
+                        FL_JSON_MESSAGE_CODEC_ERROR_INVALID_JSON);
+}
diff --git a/shell/platform/linux/fl_method_call.cc b/shell/platform/linux/fl_method_call.cc
new file mode 100644
index 0000000..0b65891
--- /dev/null
+++ b/shell/platform/linux/fl_method_call.cc
@@ -0,0 +1,119 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_call.h"
+#include "flutter/shell/platform/linux/fl_method_call_private.h"
+#include "flutter/shell/platform/linux/fl_method_channel_private.h"
+
+#include <gmodule.h>
+
+struct _FlMethodCall {
+  GObject parent_instance;
+
+  // Name of method being called
+  gchar* name;
+
+  // Arguments provided to method call
+  FlValue* args;
+
+  // Channel to respond on
+  FlMethodChannel* channel;
+  FlBinaryMessengerResponseHandle* response_handle;
+};
+
+G_DEFINE_TYPE(FlMethodCall, fl_method_call, G_TYPE_OBJECT)
+
+static void fl_method_call_dispose(GObject* object) {
+  FlMethodCall* self = FL_METHOD_CALL(object);
+
+  g_clear_pointer(&self->name, g_free);
+  g_clear_pointer(&self->args, fl_value_unref);
+  g_clear_object(&self->channel);
+  g_clear_object(&self->response_handle);
+
+  G_OBJECT_CLASS(fl_method_call_parent_class)->dispose(object);
+}
+
+static void fl_method_call_class_init(FlMethodCallClass* klass) {
+  G_OBJECT_CLASS(klass)->dispose = fl_method_call_dispose;
+}
+
+static void fl_method_call_init(FlMethodCall* self) {}
+
+FlMethodCall* fl_method_call_new(
+    const gchar* name,
+    FlValue* args,
+    FlMethodChannel* channel,
+    FlBinaryMessengerResponseHandle* response_handle) {
+  g_return_val_if_fail(name != nullptr, nullptr);
+  g_return_val_if_fail(args != nullptr, nullptr);
+  g_return_val_if_fail(FL_IS_METHOD_CHANNEL(channel), nullptr);
+  g_return_val_if_fail(FL_IS_BINARY_MESSENGER_RESPONSE_HANDLE(response_handle),
+                       nullptr);
+
+  FlMethodCall* self =
+      FL_METHOD_CALL(g_object_new(fl_method_call_get_type(), nullptr));
+
+  self->name = g_strdup(name);
+  self->args = fl_value_ref(args);
+  self->channel = FL_METHOD_CHANNEL(g_object_ref(channel));
+  self->response_handle =
+      FL_BINARY_MESSENGER_RESPONSE_HANDLE(g_object_ref(response_handle));
+
+  return self;
+}
+
+G_MODULE_EXPORT const gchar* fl_method_call_get_name(FlMethodCall* self) {
+  g_return_val_if_fail(FL_IS_METHOD_CALL(self), nullptr);
+  return self->name;
+}
+
+G_MODULE_EXPORT FlValue* fl_method_call_get_args(FlMethodCall* self) {
+  g_return_val_if_fail(FL_IS_METHOD_CALL(self), nullptr);
+  return self->args;
+}
+
+gboolean fl_method_call_respond(FlMethodCall* self,
+                                FlMethodResponse* response,
+                                GError** error) {
+  g_return_val_if_fail(FL_IS_METHOD_CALL(self), FALSE);
+  g_return_val_if_fail(FL_IS_METHOD_RESPONSE(response), FALSE);
+  return fl_method_channel_respond(self->channel, self->response_handle,
+                                   response, error);
+}
+
+G_MODULE_EXPORT gboolean fl_method_call_respond_success(FlMethodCall* self,
+                                                        FlValue* result,
+                                                        GError** error) {
+  g_return_val_if_fail(FL_IS_METHOD_CALL(self), FALSE);
+
+  g_autoptr(FlMethodResponse) response =
+      FL_METHOD_RESPONSE(fl_method_success_response_new(result));
+  return fl_method_channel_respond(self->channel, self->response_handle,
+                                   response, error);
+}
+
+G_MODULE_EXPORT gboolean fl_method_call_respond_error(FlMethodCall* self,
+                                                      const gchar* code,
+                                                      const gchar* message,
+                                                      FlValue* details,
+                                                      GError** error) {
+  g_return_val_if_fail(FL_IS_METHOD_CALL(self), FALSE);
+  g_return_val_if_fail(code != nullptr, FALSE);
+
+  g_autoptr(FlMethodResponse) response =
+      FL_METHOD_RESPONSE(fl_method_error_response_new(code, message, details));
+  return fl_method_channel_respond(self->channel, self->response_handle,
+                                   response, error);
+}
+
+G_MODULE_EXPORT gboolean
+fl_method_call_respond_not_implemented(FlMethodCall* self, GError** error) {
+  g_return_val_if_fail(FL_IS_METHOD_CALL(self), FALSE);
+
+  g_autoptr(FlMethodResponse) response =
+      FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
+  return fl_method_channel_respond(self->channel, self->response_handle,
+                                   response, error);
+}
diff --git a/shell/platform/linux/fl_method_call_private.h b/shell/platform/linux/fl_method_call_private.h
new file mode 100644
index 0000000..3388fa8
--- /dev/null
+++ b/shell/platform/linux/fl_method_call_private.h
@@ -0,0 +1,35 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CALL_PRIVATE_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CALL_PRIVATE_H_
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_call.h"
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_value.h"
+
+G_BEGIN_DECLS
+
+/**
+ * fl_method_call_new:
+ * @name: a method name.
+ * @args: arguments provided to a method.
+ * @channel: channel call received on.
+ * @response_handle: handle to respond with.
+ *
+ * Creates a method call.
+ *
+ * Returns: a new #FlMethodCall.
+ */
+FlMethodCall* fl_method_call_new(
+    const gchar* name,
+    FlValue* args,
+    FlMethodChannel* channel,
+    FlBinaryMessengerResponseHandle* response_handle);
+
+G_END_DECLS
+
+#endif  // FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CALL_PRIVATE_H_
diff --git a/shell/platform/linux/fl_method_channel.cc b/shell/platform/linux/fl_method_channel.cc
new file mode 100644
index 0000000..c8d61fa
--- /dev/null
+++ b/shell/platform/linux/fl_method_channel.cc
@@ -0,0 +1,197 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
+
+#include "flutter/shell/platform/linux/fl_method_call_private.h"
+#include "flutter/shell/platform/linux/fl_method_channel_private.h"
+#include "flutter/shell/platform/linux/fl_method_codec_private.h"
+
+#include <gmodule.h>
+
+struct _FlMethodChannel {
+  GObject parent_instance;
+
+  // Messenger to communicate on
+  FlBinaryMessenger* messenger;
+
+  // Channel name
+  gchar* name;
+
+  // Codec to en/decode messages
+  FlMethodCodec* codec;
+
+  // Function called when a method call is received
+  FlMethodChannelMethodCallHandler method_call_handler;
+  gpointer method_call_handler_data;
+};
+
+// Added here to stop the compiler from optimising this function away
+G_MODULE_EXPORT GType fl_method_channel_get_type();
+
+G_DEFINE_TYPE(FlMethodChannel, fl_method_channel, G_TYPE_OBJECT)
+
+// Called when a binary message is received on this channel
+static void message_cb(FlBinaryMessenger* messenger,
+                       const gchar* channel,
+                       GBytes* message,
+                       FlBinaryMessengerResponseHandle* response_handle,
+                       gpointer user_data) {
+  FlMethodChannel* self = FL_METHOD_CHANNEL(user_data);
+
+  if (self->method_call_handler == nullptr)
+    return;
+
+  g_autofree gchar* method = nullptr;
+  g_autoptr(FlValue) args = nullptr;
+  g_autoptr(GError) error = nullptr;
+  if (!fl_method_codec_decode_method_call(self->codec, message, &method, &args,
+                                          &error)) {
+    g_warning("Failed to decode method call: %s", error->message);
+    return;
+  }
+
+  g_autoptr(FlMethodCall) method_call =
+      fl_method_call_new(method, args, self, response_handle);
+  self->method_call_handler(self, method_call, self->method_call_handler_data);
+}
+
+// Called when a response is received to a sent message
+static void message_response_cb(GObject* object,
+                                GAsyncResult* result,
+                                gpointer user_data) {
+  GTask* task = G_TASK(user_data);
+  g_task_return_pointer(task, result, g_object_unref);
+}
+
+static void fl_method_channel_dispose(GObject* object) {
+  FlMethodChannel* self = FL_METHOD_CHANNEL(object);
+
+  if (self->messenger != nullptr)
+    fl_binary_messenger_set_message_handler_on_channel(
+        self->messenger, self->name, nullptr, nullptr);
+
+  g_clear_object(&self->messenger);
+  g_clear_pointer(&self->name, g_free);
+  g_clear_object(&self->codec);
+
+  G_OBJECT_CLASS(fl_method_channel_parent_class)->dispose(object);
+}
+
+static void fl_method_channel_class_init(FlMethodChannelClass* klass) {
+  G_OBJECT_CLASS(klass)->dispose = fl_method_channel_dispose;
+}
+
+static void fl_method_channel_init(FlMethodChannel* self) {}
+
+G_MODULE_EXPORT FlMethodChannel* fl_method_channel_new(
+    FlBinaryMessenger* messenger,
+    const gchar* name,
+    FlMethodCodec* codec) {
+  g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
+  g_return_val_if_fail(name != nullptr, nullptr);
+  g_return_val_if_fail(FL_IS_METHOD_CODEC(codec), nullptr);
+
+  FlMethodChannel* self =
+      FL_METHOD_CHANNEL(g_object_new(fl_method_channel_get_type(), nullptr));
+
+  self->messenger = FL_BINARY_MESSENGER(g_object_ref(messenger));
+  self->name = g_strdup(name);
+  self->codec = FL_METHOD_CODEC(g_object_ref(codec));
+
+  fl_binary_messenger_set_message_handler_on_channel(
+      self->messenger, self->name, message_cb, self);
+
+  return self;
+}
+
+G_MODULE_EXPORT void fl_method_channel_set_method_call_handler(
+    FlMethodChannel* self,
+    FlMethodChannelMethodCallHandler handler,
+    gpointer user_data) {
+  g_return_if_fail(FL_IS_METHOD_CHANNEL(self));
+
+  self->method_call_handler = handler;
+  self->method_call_handler_data = user_data;
+}
+
+G_MODULE_EXPORT void fl_method_channel_invoke_method(
+    FlMethodChannel* self,
+    const gchar* method,
+    FlValue* args,
+    GCancellable* cancellable,
+    GAsyncReadyCallback callback,
+    gpointer user_data) {
+  g_return_if_fail(FL_IS_METHOD_CHANNEL(self));
+  g_return_if_fail(method != nullptr);
+
+  g_autoptr(GTask) task =
+      callback != nullptr ? g_task_new(self, cancellable, callback, user_data)
+                          : nullptr;
+
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message =
+      fl_method_codec_encode_method_call(self->codec, method, args, &error);
+  if (message == nullptr) {
+    if (task != nullptr)
+      g_task_return_error(task, error);
+    return;
+  }
+
+  fl_binary_messenger_send_on_channel(
+      self->messenger, self->name, message, cancellable,
+      callback != nullptr ? message_response_cb : nullptr,
+      g_steal_pointer(&task));
+}
+
+G_MODULE_EXPORT FlMethodResponse* fl_method_channel_invoke_method_finish(
+    FlMethodChannel* self,
+    GAsyncResult* result,
+    GError** error) {
+  g_return_val_if_fail(FL_IS_METHOD_CHANNEL(self), nullptr);
+  g_return_val_if_fail(g_task_is_valid(result, self), nullptr);
+
+  g_autoptr(GTask) task = G_TASK(result);
+  GAsyncResult* r = G_ASYNC_RESULT(g_task_propagate_pointer(task, nullptr));
+
+  g_autoptr(GBytes) response =
+      fl_binary_messenger_send_on_channel_finish(self->messenger, r, error);
+  if (response == nullptr)
+    return nullptr;
+
+  return fl_method_codec_decode_response(self->codec, response, error);
+}
+
+gboolean fl_method_channel_respond(
+    FlMethodChannel* self,
+    FlBinaryMessengerResponseHandle* response_handle,
+    FlMethodResponse* response,
+    GError** error) {
+  g_return_val_if_fail(FL_IS_METHOD_CHANNEL(self), FALSE);
+  g_return_val_if_fail(FL_IS_BINARY_MESSENGER_RESPONSE_HANDLE(response_handle),
+                       FALSE);
+  g_return_val_if_fail(FL_IS_METHOD_SUCCESS_RESPONSE(response) ||
+                           FL_IS_METHOD_ERROR_RESPONSE(response) ||
+                           FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response),
+                       FALSE);
+
+  g_autoptr(GBytes) message = nullptr;
+  if (FL_IS_METHOD_SUCCESS_RESPONSE(response)) {
+    FlMethodSuccessResponse* r = FL_METHOD_SUCCESS_RESPONSE(response);
+    message = fl_method_codec_encode_success_envelope(
+        self->codec, fl_method_success_response_get_result(r), error);
+  } else if (FL_IS_METHOD_ERROR_RESPONSE(response)) {
+    FlMethodErrorResponse* r = FL_METHOD_ERROR_RESPONSE(response);
+    message = fl_method_codec_encode_error_envelope(
+        self->codec, fl_method_error_response_get_code(r),
+        fl_method_error_response_get_message(r),
+        fl_method_error_response_get_details(r), error);
+  } else if (FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response))
+    message = nullptr;
+  else
+    g_assert_not_reached();
+
+  return fl_binary_messenger_send_response(self->messenger, response_handle,
+                                           message, error);
+}
diff --git a/shell/platform/linux/fl_method_channel_private.h b/shell/platform/linux/fl_method_channel_private.h
new file mode 100644
index 0000000..f31b91f
--- /dev/null
+++ b/shell/platform/linux/fl_method_channel_private.h
@@ -0,0 +1,35 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CHANNEL_PRIVATE_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CHANNEL_PRIVATE_H_
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_response.h"
+
+G_BEGIN_DECLS
+
+/**
+ * fl_method_channel_respond:
+ * @channel: an #FlMethodChannel.
+ * @response_handle: an #FlBinaryMessengerResponseHandle.
+ * @response: an #FlMethodResponse.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Responds to a method call.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean fl_method_channel_respond(
+    FlMethodChannel* channel,
+    FlBinaryMessengerResponseHandle* response_handle,
+    FlMethodResponse* response,
+    GError** error);
+
+G_END_DECLS
+
+#endif  // FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CHANNEL_PRIVATE_H_
diff --git a/shell/platform/linux/fl_method_codec.cc b/shell/platform/linux/fl_method_codec.cc
new file mode 100644
index 0000000..11bed27
--- /dev/null
+++ b/shell/platform/linux/fl_method_codec.cc
@@ -0,0 +1,75 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h"
+#include "flutter/shell/platform/linux/fl_method_codec_private.h"
+
+#include <gmodule.h>
+
+// Added here to stop the compiler from optimising this function away
+G_MODULE_EXPORT GType fl_method_codec_get_type();
+
+G_DEFINE_TYPE(FlMethodCodec, fl_method_codec, G_TYPE_OBJECT)
+
+static void fl_method_codec_class_init(FlMethodCodecClass* klass) {}
+
+static void fl_method_codec_init(FlMethodCodec* self) {}
+
+GBytes* fl_method_codec_encode_method_call(FlMethodCodec* self,
+                                           const gchar* name,
+                                           FlValue* args,
+                                           GError** error) {
+  g_return_val_if_fail(FL_IS_METHOD_CODEC(self), nullptr);
+  g_return_val_if_fail(name != nullptr, nullptr);
+
+  return FL_METHOD_CODEC_GET_CLASS(self)->encode_method_call(self, name, args,
+                                                             error);
+}
+
+gboolean fl_method_codec_decode_method_call(FlMethodCodec* self,
+                                            GBytes* message,
+                                            gchar** name,
+                                            FlValue** args,
+                                            GError** error) {
+  g_return_val_if_fail(FL_IS_METHOD_CODEC(self), FALSE);
+  g_return_val_if_fail(message != nullptr, FALSE);
+  g_return_val_if_fail(name != nullptr, FALSE);
+  g_return_val_if_fail(args != nullptr, FALSE);
+
+  return FL_METHOD_CODEC_GET_CLASS(self)->decode_method_call(self, message,
+                                                             name, args, error);
+}
+
+GBytes* fl_method_codec_encode_success_envelope(FlMethodCodec* self,
+                                                FlValue* result,
+                                                GError** error) {
+  g_return_val_if_fail(FL_IS_METHOD_CODEC(self), nullptr);
+
+  return FL_METHOD_CODEC_GET_CLASS(self)->encode_success_envelope(self, result,
+                                                                  error);
+}
+
+GBytes* fl_method_codec_encode_error_envelope(FlMethodCodec* self,
+                                              const gchar* code,
+                                              const gchar* message,
+                                              FlValue* details,
+                                              GError** error) {
+  g_return_val_if_fail(FL_IS_METHOD_CODEC(self), nullptr);
+  g_return_val_if_fail(code != nullptr, nullptr);
+
+  return FL_METHOD_CODEC_GET_CLASS(self)->encode_error_envelope(
+      self, code, message, details, error);
+}
+
+FlMethodResponse* fl_method_codec_decode_response(FlMethodCodec* self,
+                                                  GBytes* message,
+                                                  GError** error) {
+  g_return_val_if_fail(FL_IS_METHOD_CODEC(self), nullptr);
+  g_return_val_if_fail(message != nullptr, nullptr);
+
+  if (g_bytes_get_size(message) == 0)
+    return FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
+
+  return FL_METHOD_CODEC_GET_CLASS(self)->decode_response(self, message, error);
+}
diff --git a/shell/platform/linux/fl_method_codec_private.h b/shell/platform/linux/fl_method_codec_private.h
new file mode 100644
index 0000000..8912093
--- /dev/null
+++ b/shell/platform/linux/fl_method_codec_private.h
@@ -0,0 +1,106 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CODEC_PRIVATE_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CODEC_PRIVATE_H_
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_response.h"
+
+G_BEGIN_DECLS
+
+/**
+ * fl_method_codec_encode_method_call:
+ * @codec: an #FlMethodCodec.
+ * @name: method name.
+ * @args: (allow-none): method arguments, or %NULL.
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL.
+ *
+ * Encodes a method call.
+ *
+ * Returns: (transfer full): a binary encoding of this method call or %NULL if
+ * not able to encode.
+ */
+GBytes* fl_method_codec_encode_method_call(FlMethodCodec* codec,
+                                           const gchar* name,
+                                           FlValue* args,
+                                           GError** error);
+
+/**
+ * fl_method_codec_decode_method_call:
+ * @codec: an #FlMethodCodec.
+ * @message: message to decode.
+ * @name: (transfer full): location to write method name or %NULL if not
+ * required.
+ * @args: (transfer full): location to write method arguments, or %NULL if not
+ * required.
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL.
+ *
+ * Decodes a method call.
+ *
+ * Returns: %TRUE if successfully decoded.
+ */
+gboolean fl_method_codec_decode_method_call(FlMethodCodec* codec,
+                                            GBytes* message,
+                                            gchar** name,
+                                            FlValue** args,
+                                            GError** error);
+
+/**
+ * fl_method_codec_encode_success_envelope:
+ * @codec: an #FlMethodCodec.
+ * @result: (allow-none): method result, or %NULL.
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL.
+ *
+ * Encodes a successful response to a method call.
+ *
+ * Returns: (transfer full): a binary encoding of this response or %NULL if not
+ * able to encode.
+ */
+GBytes* fl_method_codec_encode_success_envelope(FlMethodCodec* codec,
+                                                FlValue* result,
+                                                GError** error);
+
+/**
+ * fl_method_codec_encode_error_envelope:
+ * @codec: an #FlMethodCodec.
+ * @code: an error code.
+ * @message: (allow-none): an error message or %NULL.
+ * @details: (allow-none): error details, or %NULL.
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL.
+ *
+ * Encodes an error response to a method call.
+ *
+ * Returns: (transfer full): a binary encoding of this response or %NULL if not
+ * able to encode.
+ */
+GBytes* fl_method_codec_encode_error_envelope(FlMethodCodec* codec,
+                                              const gchar* code,
+                                              const gchar* message,
+                                              FlValue* details,
+                                              GError** error);
+
+/**
+ * fl_method_codec_decode_response:
+ * @codec: an #FlMethodCodec.
+ * @message: message to decode.
+ * @error: (allow-none): #GError location to store the error occurring, or
+ * %NULL.
+ *
+ * Decodes a response to a method call. If the call resulted in an error then
+ * @error_code is set, otherwise it is %NULL.
+ *
+ * Returns: a new #FlMethodResponse or %NULL on error.
+ */
+FlMethodResponse* fl_method_codec_decode_response(FlMethodCodec* codec,
+                                                  GBytes* message,
+                                                  GError** error);
+
+G_END_DECLS
+
+#endif  // FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CODEC_PRIVATE_H_
diff --git a/shell/platform/linux/fl_method_codec_test.cc b/shell/platform/linux/fl_method_codec_test.cc
new file mode 100644
index 0000000..cb216cb
--- /dev/null
+++ b/shell/platform/linux/fl_method_codec_test.cc
@@ -0,0 +1,402 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_codec.h"
+#include "flutter/shell/platform/linux/fl_method_codec_private.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h"
+#include "gtest/gtest.h"
+
+G_DECLARE_FINAL_TYPE(FlTestMethodCodec,
+                     fl_test_method_codec,
+                     FL,
+                     TEST_METHOD_CODEC,
+                     FlMethodCodec)
+
+// Implement the FlMethodCodec API for the following tests to check it works as
+// expected
+struct _FlTestMethodCodec {
+  FlMethodCodec parent_instance;
+};
+
+G_DEFINE_TYPE(FlTestMethodCodec,
+              fl_test_method_codec,
+              fl_method_codec_get_type())
+
+// Helper function to convert binary data to text
+static gchar* message_to_text(GBytes* message) {
+  size_t data_length;
+  const gchar* data =
+      static_cast<const gchar*>(g_bytes_get_data(message, &data_length));
+  return g_strndup(data, data_length);
+}
+
+// Helper function to convert text to binary data
+static GBytes* text_to_message(const gchar* text) {
+  return g_bytes_new(text, strlen(text));
+}
+
+// Implements FlMethodCodec::encode_method_call
+static GBytes* fl_test_codec_encode_method_call(FlMethodCodec* codec,
+                                                const gchar* name,
+                                                FlValue* args,
+                                                GError** error) {
+  EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec));
+
+  g_autofree gchar* text = nullptr;
+  if (args == nullptr || fl_value_get_type(args) == FL_VALUE_TYPE_NULL)
+    text = g_strdup_printf("%s()", name);
+  else if (fl_value_get_type(args) == FL_VALUE_TYPE_INT)
+    text = g_strdup_printf("%s(%" G_GINT64_FORMAT ")", name,
+                           fl_value_get_int(args));
+  else {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                "ERROR");
+    return nullptr;
+  }
+
+  return text_to_message(text);
+}
+
+// Implements FlMethodCodec::decode_method_call
+static gboolean fl_test_codec_decode_method_call(FlMethodCodec* codec,
+                                                 GBytes* message,
+                                                 gchar** name,
+                                                 FlValue** args,
+                                                 GError** error) {
+  EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec));
+
+  g_autofree gchar* m = message_to_text(message);
+
+  if (strcmp(m, "error") == 0) {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                "ERROR");
+    return FALSE;
+  } else {
+    *name = g_strdup(m);
+    *args = fl_value_new_null();
+    return TRUE;
+  }
+}
+
+// Implements FlMethodCodec::encode_success_envelope
+static GBytes* fl_test_codec_encode_success_envelope(FlMethodCodec* codec,
+                                                     FlValue* result,
+                                                     GError** error) {
+  EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec));
+
+  g_autofree gchar* text = nullptr;
+  if (result == nullptr || fl_value_get_type(result) == FL_VALUE_TYPE_NULL)
+    text = g_strdup("(null)");
+  else if (fl_value_get_type(result) == FL_VALUE_TYPE_INT)
+    text = g_strdup_printf("%" G_GINT64_FORMAT, fl_value_get_int(result));
+  else {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                "ERROR");
+    return nullptr;
+  }
+
+  return text_to_message(text);
+}
+
+// Implements FlMethodCodec::encode_error_envelope
+static GBytes* fl_test_codec_encode_error_envelope(FlMethodCodec* codec,
+                                                   const gchar* code,
+                                                   const gchar* message,
+                                                   FlValue* details,
+                                                   GError** error) {
+  EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec));
+
+  if (details != nullptr && fl_value_get_type(details) != FL_VALUE_TYPE_INT) {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                "ERROR");
+    return nullptr;
+  }
+
+  g_autofree gchar* text = nullptr;
+  if (message == nullptr) {
+    if (details == nullptr || fl_value_get_type(details) == FL_VALUE_TYPE_NULL)
+      text = g_strdup_printf("Error_%s()", code);
+    else
+      text = g_strdup_printf("Error_%s(%" G_GINT64_FORMAT ")", code,
+                             fl_value_get_int(details));
+  } else {
+    if (details == nullptr || fl_value_get_type(details) == FL_VALUE_TYPE_NULL)
+      text = g_strdup_printf("Error_%s(%s)", code, message);
+    else
+      text = g_strdup_printf("Error_%s(%s,%" G_GINT64_FORMAT ")", code, message,
+                             fl_value_get_int(details));
+  }
+
+  return text_to_message(text);
+}
+
+// Implements FlMethodCodec::decode_response
+static FlMethodResponse* fl_test_codec_decode_response(FlMethodCodec* codec,
+                                                       GBytes* message,
+                                                       GError** error) {
+  EXPECT_TRUE(FL_IS_TEST_METHOD_CODEC(codec));
+
+  g_autofree gchar* m = message_to_text(message);
+  if (strcmp(m, "codec-error") == 0) {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                "ERROR");
+    return nullptr;
+  } else if (strcmp(m, "error") == 0) {
+    g_autoptr(FlValue) details = fl_value_new_int(42);
+    return FL_METHOD_RESPONSE(
+        fl_method_error_response_new("code", "message", details));
+  } else {
+    g_autoptr(FlValue) result = fl_value_new_string(m);
+    return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
+  }
+}
+
+static void fl_test_method_codec_class_init(FlTestMethodCodecClass* klass) {
+  FL_METHOD_CODEC_CLASS(klass)->encode_method_call =
+      fl_test_codec_encode_method_call;
+  FL_METHOD_CODEC_CLASS(klass)->decode_method_call =
+      fl_test_codec_decode_method_call;
+  FL_METHOD_CODEC_CLASS(klass)->encode_success_envelope =
+      fl_test_codec_encode_success_envelope;
+  FL_METHOD_CODEC_CLASS(klass)->encode_error_envelope =
+      fl_test_codec_encode_error_envelope;
+  FL_METHOD_CODEC_CLASS(klass)->decode_response = fl_test_codec_decode_response;
+}
+
+static void fl_test_method_codec_init(FlTestMethodCodec* self) {}
+
+static FlTestMethodCodec* fl_test_method_codec_new() {
+  return FL_TEST_METHOD_CODEC(
+      g_object_new(fl_test_method_codec_get_type(), nullptr));
+}
+
+TEST(FlMethodCodecTest, EncodeMethodCall) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
+      FL_METHOD_CODEC(codec), "foo", nullptr, &error);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_NE(message, nullptr);
+
+  g_autofree gchar* message_text = message_to_text(message);
+  EXPECT_STREQ(message_text, "foo()");
+}
+
+TEST(FlMethodCodecTest, EncodeMethodCallEmptyName) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
+      FL_METHOD_CODEC(codec), "", nullptr, &error);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_NE(message, nullptr);
+
+  g_autofree gchar* message_text = message_to_text(message);
+  EXPECT_STREQ(message_text, "()");
+}
+
+TEST(FlMethodCodecTest, EncodeMethodCallArgs) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(FlValue) args = fl_value_new_int(42);
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
+      FL_METHOD_CODEC(codec), "foo", args, &error);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_NE(message, nullptr);
+
+  g_autofree gchar* message_text = message_to_text(message);
+  EXPECT_STREQ(message_text, "foo(42)");
+}
+
+TEST(FlMethodCodecTest, EncodeMethodCallError) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(FlValue) args = fl_value_new_bool(FALSE);
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
+      FL_METHOD_CODEC(codec), "foo", args, &error);
+  EXPECT_EQ(message, nullptr);
+  EXPECT_TRUE(g_error_matches(error, FL_MESSAGE_CODEC_ERROR,
+                              FL_MESSAGE_CODEC_ERROR_FAILED));
+}
+
+TEST(FlMethodCodecTest, DecodeMethodCall) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(GBytes) message = text_to_message("foo");
+
+  g_autofree gchar* name = nullptr;
+  g_autoptr(FlValue) args = nullptr;
+  g_autoptr(GError) error = nullptr;
+  gboolean result = fl_method_codec_decode_method_call(
+      FL_METHOD_CODEC(codec), message, &name, &args, &error);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_TRUE(result);
+
+  EXPECT_STREQ(name, "foo");
+  ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_NULL);
+}
+
+TEST(FlMethodCodecTest, EncodeSuccessEnvelope) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(FlValue) result = fl_value_new_int(42);
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_success_envelope(
+      FL_METHOD_CODEC(codec), result, &error);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_NE(message, nullptr);
+
+  g_autofree gchar* message_text = message_to_text(message);
+  EXPECT_STREQ(message_text, "42");
+}
+
+TEST(FlMethodCodecTest, EncodeSuccessEnvelopeEmpty) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_success_envelope(
+      FL_METHOD_CODEC(codec), nullptr, &error);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_NE(message, nullptr);
+
+  g_autofree gchar* message_text = message_to_text(message);
+  EXPECT_STREQ(message_text, "(null)");
+}
+
+TEST(FlMethodCodecTest, EncodeSuccessEnvelopeError) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(FlValue) result = fl_value_new_string("X");
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_success_envelope(
+      FL_METHOD_CODEC(codec), result, &error);
+  EXPECT_EQ(message, nullptr);
+  EXPECT_TRUE(g_error_matches(error, FL_MESSAGE_CODEC_ERROR,
+                              FL_MESSAGE_CODEC_ERROR_FAILED));
+}
+
+TEST(FlMethodCodecTest, EncodeErrorEnvelopeNoMessageOrDetails) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope(
+      FL_METHOD_CODEC(codec), "code", nullptr, nullptr, &error);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_NE(message, nullptr);
+
+  g_autofree gchar* message_text = message_to_text(message);
+  EXPECT_STREQ(message_text, "Error_code()");
+}
+
+TEST(FlMethodCodecTest, EncodeErrorEnvelopeMessage) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope(
+      FL_METHOD_CODEC(codec), "code", "message", nullptr, &error);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_NE(message, nullptr);
+
+  g_autofree gchar* message_text = message_to_text(message);
+  EXPECT_STREQ(message_text, "Error_code(message)");
+}
+
+TEST(FlMethodCodecTest, EncodeErrorEnvelopeDetails) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(FlValue) details = fl_value_new_int(42);
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope(
+      FL_METHOD_CODEC(codec), "code", nullptr, details, &error);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_NE(message, nullptr);
+
+  g_autofree gchar* message_text = message_to_text(message);
+  EXPECT_STREQ(message_text, "Error_code(42)");
+}
+
+TEST(FlMethodCodecTest, EncodeErrorEnvelopeMessageAndDetails) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(FlValue) details = fl_value_new_int(42);
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope(
+      FL_METHOD_CODEC(codec), "code", "message", details, &error);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_NE(message, nullptr);
+
+  g_autofree gchar* message_text = message_to_text(message);
+  EXPECT_STREQ(message_text, "Error_code(message,42)");
+}
+
+TEST(FlMethodCodecTest, DecodeResponseSuccess) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(GBytes) message = text_to_message("echo");
+
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlMethodResponse) response =
+      fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_NE(response, nullptr);
+  ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
+  FlValue* result = fl_method_success_response_get_result(
+      FL_METHOD_SUCCESS_RESPONSE(response));
+  ASSERT_NE(result, nullptr);
+  ASSERT_EQ(fl_value_get_type(result), FL_VALUE_TYPE_STRING);
+  EXPECT_STREQ(fl_value_get_string(result), "echo");
+}
+
+TEST(FlMethodCodecTest, DecodeResponseNotImplemented) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(GBytes) message = g_bytes_new(nullptr, 0);
+
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlMethodResponse) response =
+      fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_NE(response, nullptr);
+  ASSERT_TRUE(FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response));
+}
+
+TEST(FlMethodCodecTest, DecodeResponseCodecError) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(GBytes) message = text_to_message("codec-error");
+
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlMethodResponse) response =
+      fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+  EXPECT_TRUE(g_error_matches(error, FL_MESSAGE_CODEC_ERROR,
+                              FL_MESSAGE_CODEC_ERROR_FAILED));
+  EXPECT_EQ(response, nullptr);
+}
+
+TEST(FlMethodCodecTest, DecodeResponseError) {
+  g_autoptr(FlTestMethodCodec) codec = fl_test_method_codec_new();
+
+  g_autoptr(GBytes) message = text_to_message("error");
+
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlMethodResponse) response =
+      fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_NE(response, nullptr);
+  ASSERT_TRUE(FL_METHOD_ERROR_RESPONSE(response));
+  EXPECT_STREQ(
+      fl_method_error_response_get_code(FL_METHOD_ERROR_RESPONSE(response)),
+      "code");
+  EXPECT_STREQ(
+      fl_method_error_response_get_message(FL_METHOD_ERROR_RESPONSE(response)),
+      "message");
+  FlValue* details =
+      fl_method_error_response_get_details(FL_METHOD_ERROR_RESPONSE(response));
+  ASSERT_NE(details, nullptr);
+  ASSERT_EQ(fl_value_get_type(details), FL_VALUE_TYPE_INT);
+  EXPECT_EQ(fl_value_get_int(details), 42);
+}
diff --git a/shell/platform/linux/fl_method_response.cc b/shell/platform/linux/fl_method_response.cc
new file mode 100644
index 0000000..9082b9d
--- /dev/null
+++ b/shell/platform/linux/fl_method_response.cc
@@ -0,0 +1,178 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_response.h"
+
+#include <gmodule.h>
+
+G_DEFINE_QUARK(fl_method_response_error_quark, fl_method_response_error)
+
+struct _FlMethodSuccessResponse {
+  FlMethodResponse parent_instance;
+
+  FlValue* result;
+};
+
+struct _FlMethodErrorResponse {
+  FlMethodResponse parent_instance;
+
+  gchar* code;
+  gchar* message;
+  FlValue* details;
+};
+
+struct _FlMethodNotImplementedResponse {
+  FlMethodResponse parent_instance;
+};
+
+G_DEFINE_TYPE(FlMethodResponse, fl_method_response, G_TYPE_OBJECT)
+G_DEFINE_TYPE(FlMethodSuccessResponse,
+              fl_method_success_response,
+              fl_method_response_get_type())
+G_DEFINE_TYPE(FlMethodErrorResponse,
+              fl_method_error_response,
+              fl_method_response_get_type())
+G_DEFINE_TYPE(FlMethodNotImplementedResponse,
+              fl_method_not_implemented_response,
+              fl_method_response_get_type())
+
+static void fl_method_response_class_init(FlMethodResponseClass* klass) {}
+
+static void fl_method_response_init(FlMethodResponse* self) {}
+
+static void fl_method_success_response_dispose(GObject* object) {
+  FlMethodSuccessResponse* self = FL_METHOD_SUCCESS_RESPONSE(object);
+
+  g_clear_pointer(&self->result, fl_value_unref);
+
+  G_OBJECT_CLASS(fl_method_success_response_parent_class)->dispose(object);
+}
+
+static void fl_method_success_response_class_init(
+    FlMethodSuccessResponseClass* klass) {
+  G_OBJECT_CLASS(klass)->dispose = fl_method_success_response_dispose;
+}
+
+static void fl_method_success_response_init(FlMethodSuccessResponse* self) {}
+
+static void fl_method_error_response_dispose(GObject* object) {
+  FlMethodErrorResponse* self = FL_METHOD_ERROR_RESPONSE(object);
+
+  g_clear_pointer(&self->code, g_free);
+  g_clear_pointer(&self->message, g_free);
+  g_clear_pointer(&self->details, fl_value_unref);
+
+  G_OBJECT_CLASS(fl_method_error_response_parent_class)->dispose(object);
+}
+
+static void fl_method_error_response_class_init(
+    FlMethodErrorResponseClass* klass) {
+  G_OBJECT_CLASS(klass)->dispose = fl_method_error_response_dispose;
+}
+
+static void fl_method_error_response_init(FlMethodErrorResponse* self) {}
+
+static void fl_method_not_implemented_response_class_init(
+    FlMethodNotImplementedResponseClass* klass) {}
+
+static void fl_method_not_implemented_response_init(
+    FlMethodNotImplementedResponse* self) {}
+
+G_MODULE_EXPORT FlValue* fl_method_response_get_result(FlMethodResponse* self,
+                                                       GError** error) {
+  if (FL_IS_METHOD_SUCCESS_RESPONSE(self))
+    return fl_method_success_response_get_result(
+        FL_METHOD_SUCCESS_RESPONSE(self));
+
+  if (FL_IS_METHOD_ERROR_RESPONSE(self)) {
+    const gchar* code =
+        fl_method_error_response_get_code(FL_METHOD_ERROR_RESPONSE(self));
+    const gchar* message =
+        fl_method_error_response_get_message(FL_METHOD_ERROR_RESPONSE(self));
+    FlValue* details =
+        fl_method_error_response_get_details(FL_METHOD_ERROR_RESPONSE(self));
+    g_autofree gchar* details_text = nullptr;
+    if (details != nullptr) {
+      // TODO(robert-ancell): Update this when we have fl_value_to_string()
+    }
+
+    g_autoptr(GString) error_message = g_string_new("");
+    g_string_append_printf(error_message, "Remote code returned error %s",
+                           code);
+    if (message != nullptr)
+      g_string_append_printf(error_message, ": %s", message);
+    if (details_text != nullptr)
+      g_string_append_printf(error_message, " %s", details_text);
+    g_set_error_literal(error, FL_METHOD_RESPONSE_ERROR,
+                        FL_METHOD_RESPONSE_ERROR_REMOTE_ERROR,
+                        error_message->str);
+    return nullptr;
+  } else if (FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(self)) {
+    g_set_error(error, FL_METHOD_RESPONSE_ERROR,
+                FL_METHOD_RESPONSE_ERROR_NOT_IMPLEMENTED,
+                "Requested method is not implemented");
+    return nullptr;
+  } else {
+    g_set_error(error, FL_METHOD_RESPONSE_ERROR,
+                FL_METHOD_RESPONSE_ERROR_FAILED, "Unknown response type");
+    return nullptr;
+  }
+}
+
+G_MODULE_EXPORT FlMethodSuccessResponse* fl_method_success_response_new(
+    FlValue* result) {
+  FlMethodSuccessResponse* self = FL_METHOD_SUCCESS_RESPONSE(
+      g_object_new(fl_method_success_response_get_type(), nullptr));
+
+  if (result != nullptr)
+    self->result = fl_value_ref(result);
+
+  return self;
+}
+
+G_MODULE_EXPORT FlValue* fl_method_success_response_get_result(
+    FlMethodSuccessResponse* self) {
+  g_return_val_if_fail(FL_IS_METHOD_SUCCESS_RESPONSE(self), nullptr);
+  return self->result;
+}
+
+G_MODULE_EXPORT FlMethodErrorResponse* fl_method_error_response_new(
+    const gchar* code,
+    const gchar* message,
+    FlValue* details) {
+  g_return_val_if_fail(code != nullptr, nullptr);
+
+  FlMethodErrorResponse* self = FL_METHOD_ERROR_RESPONSE(
+      g_object_new(fl_method_error_response_get_type(), nullptr));
+
+  self->code = g_strdup(code);
+  self->message = g_strdup(message);
+  self->details = details != nullptr ? fl_value_ref(details) : nullptr;
+
+  return self;
+}
+
+G_MODULE_EXPORT const gchar* fl_method_error_response_get_code(
+    FlMethodErrorResponse* self) {
+  g_return_val_if_fail(FL_IS_METHOD_ERROR_RESPONSE(self), nullptr);
+  return self->code;
+}
+
+G_MODULE_EXPORT const gchar* fl_method_error_response_get_message(
+    FlMethodErrorResponse* self) {
+  g_return_val_if_fail(FL_IS_METHOD_ERROR_RESPONSE(self), nullptr);
+  return self->message;
+}
+
+G_MODULE_EXPORT FlValue* fl_method_error_response_get_details(
+    FlMethodErrorResponse* self) {
+  g_return_val_if_fail(FL_IS_METHOD_ERROR_RESPONSE(self), nullptr);
+  return self->details;
+}
+
+G_MODULE_EXPORT FlMethodNotImplementedResponse*
+fl_method_not_implemented_response_new() {
+  return FL_METHOD_NOT_IMPLEMENTED_RESPONSE(
+      g_object_new(fl_method_not_implemented_response_get_type(), nullptr));
+}
diff --git a/shell/platform/linux/fl_method_response_test.cc b/shell/platform/linux/fl_method_response_test.cc
new file mode 100644
index 0000000..0309a30
--- /dev/null
+++ b/shell/platform/linux/fl_method_response_test.cc
@@ -0,0 +1,96 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_response.h"
+#include "gtest/gtest.h"
+
+TEST(FlMethodResponseTest, Success) {
+  g_autoptr(FlValue) result = fl_value_new_int(42);
+  g_autoptr(FlMethodSuccessResponse) response =
+      fl_method_success_response_new(result);
+  g_autoptr(FlValue) expected = fl_value_new_int(42);
+  ASSERT_TRUE(fl_value_equal(fl_method_success_response_get_result(response),
+                             expected));
+}
+
+TEST(FlMethodResponseTest, Error) {
+  g_autoptr(FlMethodErrorResponse) response =
+      fl_method_error_response_new("code", nullptr, nullptr);
+  EXPECT_STREQ(fl_method_error_response_get_code(response), "code");
+  EXPECT_EQ(fl_method_error_response_get_message(response), nullptr);
+  EXPECT_EQ(fl_method_error_response_get_details(response), nullptr);
+}
+
+TEST(FlMethodResponseTest, ErrorMessage) {
+  g_autoptr(FlMethodErrorResponse) response =
+      fl_method_error_response_new("code", "message", nullptr);
+  EXPECT_STREQ(fl_method_error_response_get_code(response), "code");
+  EXPECT_STREQ(fl_method_error_response_get_message(response), "message");
+  EXPECT_EQ(fl_method_error_response_get_details(response), nullptr);
+}
+
+TEST(FlMethodResponseTest, ErrorDetails) {
+  g_autoptr(FlValue) details = fl_value_new_int(42);
+  g_autoptr(FlMethodErrorResponse) response =
+      fl_method_error_response_new("code", nullptr, details);
+  EXPECT_STREQ(fl_method_error_response_get_code(response), "code");
+  EXPECT_EQ(fl_method_error_response_get_message(response), nullptr);
+  g_autoptr(FlValue) expected_details = fl_value_new_int(42);
+  EXPECT_TRUE(fl_value_equal(fl_method_error_response_get_details(response),
+                             expected_details));
+}
+
+TEST(FlMethodResponseTest, ErrorMessageAndDetails) {
+  g_autoptr(FlValue) details = fl_value_new_int(42);
+  g_autoptr(FlMethodErrorResponse) response =
+      fl_method_error_response_new("code", "message", details);
+  EXPECT_STREQ(fl_method_error_response_get_code(response), "code");
+  EXPECT_STREQ(fl_method_error_response_get_message(response), "message");
+  g_autoptr(FlValue) expected_details = fl_value_new_int(42);
+  EXPECT_TRUE(fl_value_equal(fl_method_error_response_get_details(response),
+                             expected_details));
+}
+
+TEST(FlMethodResponseTest, NotImplemented) {
+  g_autoptr(FlMethodNotImplementedResponse) response =
+      fl_method_not_implemented_response_new();
+  // Trivial check to stop the compiler deciding that 'response' is an unused
+  // variable
+  EXPECT_TRUE(FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response));
+}
+
+TEST(FlMethodResponseTest, SuccessGetResult) {
+  g_autoptr(FlValue) r = fl_value_new_int(42);
+  g_autoptr(FlMethodSuccessResponse) response =
+      fl_method_success_response_new(r);
+  g_autoptr(GError) error = nullptr;
+  FlValue* result =
+      fl_method_response_get_result(FL_METHOD_RESPONSE(response), &error);
+  ASSERT_NE(result, nullptr);
+  EXPECT_EQ(error, nullptr);
+  g_autoptr(FlValue) expected = fl_value_new_int(42);
+  ASSERT_TRUE(fl_value_equal(result, expected));
+}
+
+TEST(FlMethodResponseTest, ErrorGetResult) {
+  g_autoptr(FlMethodErrorResponse) response =
+      fl_method_error_response_new("code", nullptr, nullptr);
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlValue) result =
+      fl_method_response_get_result(FL_METHOD_RESPONSE(response), &error);
+  EXPECT_EQ(result, nullptr);
+  EXPECT_TRUE(g_error_matches(error, FL_METHOD_RESPONSE_ERROR,
+                              FL_METHOD_RESPONSE_ERROR_REMOTE_ERROR));
+}
+
+TEST(FlMethodResponseTest, NotImplementedGetResult) {
+  g_autoptr(FlMethodNotImplementedResponse) response =
+      fl_method_not_implemented_response_new();
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlValue) result =
+      fl_method_response_get_result(FL_METHOD_RESPONSE(response), &error);
+  EXPECT_EQ(result, nullptr);
+  EXPECT_TRUE(g_error_matches(error, FL_METHOD_RESPONSE_ERROR,
+                              FL_METHOD_RESPONSE_ERROR_NOT_IMPLEMENTED));
+}
diff --git a/shell/platform/linux/fl_standard_method_codec.cc b/shell/platform/linux/fl_standard_method_codec.cc
new file mode 100644
index 0000000..429b35a
--- /dev/null
+++ b/shell/platform/linux/fl_standard_method_codec.cc
@@ -0,0 +1,238 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
+
+#include "flutter/shell/platform/linux/fl_standard_message_codec_private.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h"
+
+#include <gmodule.h>
+
+// See lib/src/services/message_codecs.dart in Flutter source for description of
+// encoding
+
+// Envelope codes
+static constexpr guint8 kEnvelopeTypeSuccess = 0;
+static constexpr guint8 kEnvelopeTypeError = 1;
+
+struct _FlStandardMethodCodec {
+  FlMethodCodec parent_instance;
+
+  FlStandardMessageCodec* codec;
+};
+
+G_DEFINE_TYPE(FlStandardMethodCodec,
+              fl_standard_method_codec,
+              fl_method_codec_get_type())
+
+static void fl_standard_method_codec_dispose(GObject* object) {
+  FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(object);
+
+  g_clear_object(&self->codec);
+
+  G_OBJECT_CLASS(fl_standard_method_codec_parent_class)->dispose(object);
+}
+
+// Implements FlMethodCodec::encode_method_code
+static GBytes* fl_standard_method_codec_encode_method_call(FlMethodCodec* codec,
+                                                           const gchar* name,
+                                                           FlValue* args,
+                                                           GError** error) {
+  FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(codec);
+
+  g_autoptr(GByteArray) buffer = g_byte_array_new();
+  g_autoptr(FlValue) name_value = fl_value_new_string(name);
+  if (!fl_standard_message_codec_write_value(self->codec, buffer, name_value,
+                                             error))
+    return nullptr;
+  if (!fl_standard_message_codec_write_value(self->codec, buffer, args, error))
+    return nullptr;
+
+  return g_byte_array_free_to_bytes(
+      static_cast<GByteArray*>(g_steal_pointer(&buffer)));
+}
+
+// Implements FlMethodCodec::decode_method_code
+static gboolean fl_standard_method_codec_decode_method_call(
+    FlMethodCodec* codec,
+    GBytes* message,
+    gchar** name,
+    FlValue** args,
+    GError** error) {
+  FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(codec);
+
+  size_t offset = 0;
+  g_autoptr(FlValue) name_value = fl_standard_message_codec_read_value(
+      self->codec, message, &offset, error);
+  if (name_value == nullptr)
+    return FALSE;
+  if (fl_value_get_type(name_value) != FL_VALUE_TYPE_STRING) {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                "Method call name wrong type");
+    return FALSE;
+  }
+
+  g_autoptr(FlValue) args_value = fl_standard_message_codec_read_value(
+      self->codec, message, &offset, error);
+  if (args_value == nullptr)
+    return FALSE;
+
+  if (offset != g_bytes_get_size(message)) {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                "Unexpected extra data");
+    return FALSE;
+  }
+
+  *name = g_strdup(fl_value_get_string(name_value));
+  *args = fl_value_ref(args_value);
+
+  return TRUE;
+}
+
+// Implements FlMethodCodec::encode_success_envelope
+static GBytes* fl_standard_method_codec_encode_success_envelope(
+    FlMethodCodec* codec,
+    FlValue* result,
+    GError** error) {
+  FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(codec);
+
+  g_autoptr(GByteArray) buffer = g_byte_array_new();
+  guint8 type = kEnvelopeTypeSuccess;
+  g_byte_array_append(buffer, &type, 1);
+  if (!fl_standard_message_codec_write_value(self->codec, buffer, result,
+                                             error))
+    return nullptr;
+
+  return g_byte_array_free_to_bytes(
+      static_cast<GByteArray*>(g_steal_pointer(&buffer)));
+}
+
+// Implements FlMethodCodec::encode_error_envelope
+static GBytes* fl_standard_method_codec_encode_error_envelope(
+    FlMethodCodec* codec,
+    const gchar* code,
+    const gchar* message,
+    FlValue* details,
+    GError** error) {
+  FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(codec);
+
+  g_autoptr(GByteArray) buffer = g_byte_array_new();
+  guint8 type = kEnvelopeTypeError;
+  g_byte_array_append(buffer, &type, 1);
+  g_autoptr(FlValue) code_value = fl_value_new_string(code);
+  if (!fl_standard_message_codec_write_value(self->codec, buffer, code_value,
+                                             error))
+    return nullptr;
+  g_autoptr(FlValue) message_value =
+      message != nullptr ? fl_value_new_string(message) : nullptr;
+  if (!fl_standard_message_codec_write_value(self->codec, buffer, message_value,
+                                             error))
+    return nullptr;
+  if (!fl_standard_message_codec_write_value(self->codec, buffer, details,
+                                             error))
+    return nullptr;
+
+  return g_byte_array_free_to_bytes(
+      static_cast<GByteArray*>(g_steal_pointer(&buffer)));
+}
+
+// Implements FlMethodCodec::encode_decode_reponse
+static FlMethodResponse* fl_standard_method_codec_decode_response(
+    FlMethodCodec* codec,
+    GBytes* message,
+    GError** error) {
+  FlStandardMethodCodec* self = FL_STANDARD_METHOD_CODEC(codec);
+
+  if (g_bytes_get_size(message) == 0) {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR,
+                FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA, "Empty response");
+    return nullptr;
+  }
+
+  // First byte is response type
+  const guint8* data =
+      static_cast<const guint8*>(g_bytes_get_data(message, nullptr));
+  guint8 type = data[0];
+  size_t offset = 1;
+
+  g_autoptr(FlMethodResponse) response = nullptr;
+  if (type == kEnvelopeTypeError) {
+    g_autoptr(FlValue) code = fl_standard_message_codec_read_value(
+        self->codec, message, &offset, error);
+    if (code == nullptr)
+      return nullptr;
+    if (fl_value_get_type(code) != FL_VALUE_TYPE_STRING) {
+      g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                  "Error code wrong type");
+      return nullptr;
+    }
+
+    g_autoptr(FlValue) error_message = fl_standard_message_codec_read_value(
+        self->codec, message, &offset, error);
+    if (error_message == nullptr)
+      return nullptr;
+    if (fl_value_get_type(error_message) != FL_VALUE_TYPE_STRING &&
+        fl_value_get_type(error_message) != FL_VALUE_TYPE_NULL) {
+      g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                  "Error message wrong type");
+      return nullptr;
+    }
+
+    g_autoptr(FlValue) details = fl_standard_message_codec_read_value(
+        self->codec, message, &offset, error);
+    if (details == nullptr)
+      return nullptr;
+
+    response = FL_METHOD_RESPONSE(fl_method_error_response_new(
+        fl_value_get_string(code),
+        fl_value_get_type(error_message) == FL_VALUE_TYPE_STRING
+            ? fl_value_get_string(error_message)
+            : nullptr,
+        fl_value_get_type(details) != FL_VALUE_TYPE_NULL ? details : nullptr));
+  } else if (type == kEnvelopeTypeSuccess) {
+    g_autoptr(FlValue) result = fl_standard_message_codec_read_value(
+        self->codec, message, &offset, error);
+
+    if (result == nullptr)
+      return nullptr;
+
+    response = FL_METHOD_RESPONSE(fl_method_success_response_new(result));
+  } else {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                "Unknown envelope type %02x", type);
+    return nullptr;
+  }
+
+  if (offset != g_bytes_get_size(message)) {
+    g_set_error(error, FL_MESSAGE_CODEC_ERROR, FL_MESSAGE_CODEC_ERROR_FAILED,
+                "Unexpected extra data");
+    return nullptr;
+  }
+
+  return FL_METHOD_RESPONSE(g_object_ref(response));
+}
+
+static void fl_standard_method_codec_class_init(
+    FlStandardMethodCodecClass* klass) {
+  G_OBJECT_CLASS(klass)->dispose = fl_standard_method_codec_dispose;
+  FL_METHOD_CODEC_CLASS(klass)->encode_method_call =
+      fl_standard_method_codec_encode_method_call;
+  FL_METHOD_CODEC_CLASS(klass)->decode_method_call =
+      fl_standard_method_codec_decode_method_call;
+  FL_METHOD_CODEC_CLASS(klass)->encode_success_envelope =
+      fl_standard_method_codec_encode_success_envelope;
+  FL_METHOD_CODEC_CLASS(klass)->encode_error_envelope =
+      fl_standard_method_codec_encode_error_envelope;
+  FL_METHOD_CODEC_CLASS(klass)->decode_response =
+      fl_standard_method_codec_decode_response;
+}
+
+static void fl_standard_method_codec_init(FlStandardMethodCodec* self) {
+  self->codec = fl_standard_message_codec_new();
+}
+
+G_MODULE_EXPORT FlStandardMethodCodec* fl_standard_method_codec_new() {
+  return FL_STANDARD_METHOD_CODEC(
+      g_object_new(fl_standard_method_codec_get_type(), nullptr));
+}
diff --git a/shell/platform/linux/fl_standard_method_codec_test.cc b/shell/platform/linux/fl_standard_method_codec_test.cc
new file mode 100644
index 0000000..ef0b554
--- /dev/null
+++ b/shell/platform/linux/fl_standard_method_codec_test.cc
@@ -0,0 +1,375 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
+#include "flutter/shell/platform/linux/fl_method_codec_private.h"
+#include "flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h"
+#include "flutter/shell/platform/linux/testing/fl_test.h"
+#include "gtest/gtest.h"
+
+// NOTE(robert-ancell) These test cases assumes a little-endian architecture.
+// These tests will need to be updated if tested on a big endian architecture.
+
+// Encodes a method call using StandardMethodCodec to a hex string.
+static gchar* encode_method_call(const gchar* name, FlValue* args) {
+  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
+      FL_METHOD_CODEC(codec), name, args, &error);
+  EXPECT_NE(message, nullptr);
+  EXPECT_EQ(error, nullptr);
+
+  return bytes_to_hex_string(message);
+}
+
+// Encodes a success envelope response using StandardMethodCodec to a hex
+// string.
+static gchar* encode_success_envelope(FlValue* result) {
+  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_success_envelope(
+      FL_METHOD_CODEC(codec), result, &error);
+  EXPECT_NE(message, nullptr);
+  EXPECT_EQ(error, nullptr);
+
+  return bytes_to_hex_string(message);
+}
+
+// Encodes a error envelope response using StandardMethodCodec to a hex string.
+static gchar* encode_error_envelope(const gchar* error_code,
+                                    const gchar* error_message,
+                                    FlValue* details) {
+  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(GBytes) message = fl_method_codec_encode_error_envelope(
+      FL_METHOD_CODEC(codec), error_code, error_message, details, &error);
+  EXPECT_NE(message, nullptr);
+  EXPECT_EQ(error, nullptr);
+
+  return bytes_to_hex_string(message);
+}
+
+// Decodes a method call using StandardMethodCodec with a hex string.
+static void decode_method_call(const char* hex_string,
+                               gchar** name,
+                               FlValue** args) {
+  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+  g_autoptr(GBytes) message = hex_string_to_bytes(hex_string);
+  g_autoptr(GError) error = nullptr;
+  gboolean result = fl_method_codec_decode_method_call(
+      FL_METHOD_CODEC(codec), message, name, args, &error);
+  EXPECT_TRUE(result);
+  EXPECT_EQ(error, nullptr);
+}
+
+// Decodes a method call using StandardMethodCodec. Expect the given error.
+static void decode_error_method_call(const char* hex_string,
+                                     GQuark domain,
+                                     gint code) {
+  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+  g_autoptr(GBytes) message = hex_string_to_bytes(hex_string);
+  g_autoptr(GError) error = nullptr;
+  g_autofree gchar* name = nullptr;
+  g_autoptr(FlValue) args = nullptr;
+  gboolean result = fl_method_codec_decode_method_call(
+      FL_METHOD_CODEC(codec), message, &name, &args, &error);
+  EXPECT_FALSE(result);
+  EXPECT_EQ(name, nullptr);
+  EXPECT_EQ(args, nullptr);
+  EXPECT_TRUE(g_error_matches(error, domain, code));
+}
+
+// Decodes a response using StandardMethodCodec. Expect the response is a
+// result.
+static void decode_response_with_success(const char* hex_string,
+                                         FlValue* result) {
+  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+  g_autoptr(GBytes) message = hex_string_to_bytes(hex_string);
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlMethodResponse) response =
+      fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+  ASSERT_NE(response, nullptr);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
+  EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result(
+                                 FL_METHOD_SUCCESS_RESPONSE(response)),
+                             result));
+}
+
+// Decodes a response using StandardMethodCodec. Expect the response contains
+// the given error.
+static void decode_response_with_error(const char* hex_string,
+                                       const gchar* code,
+                                       const gchar* error_message,
+                                       FlValue* details) {
+  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+  g_autoptr(GBytes) message = hex_string_to_bytes(hex_string);
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlMethodResponse) response =
+      fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+  ASSERT_NE(response, nullptr);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_TRUE(FL_IS_METHOD_ERROR_RESPONSE(response));
+  EXPECT_STREQ(
+      fl_method_error_response_get_code(FL_METHOD_ERROR_RESPONSE(response)),
+      code);
+  if (error_message == nullptr)
+    EXPECT_EQ(fl_method_error_response_get_message(
+                  FL_METHOD_ERROR_RESPONSE(response)),
+              nullptr);
+  else
+    EXPECT_STREQ(fl_method_error_response_get_message(
+                     FL_METHOD_ERROR_RESPONSE(response)),
+                 error_message);
+  if (details == nullptr)
+    EXPECT_EQ(fl_method_error_response_get_details(
+                  FL_METHOD_ERROR_RESPONSE(response)),
+              nullptr);
+  else
+    EXPECT_TRUE(fl_value_equal(fl_method_error_response_get_details(
+                                   FL_METHOD_ERROR_RESPONSE(response)),
+                               details));
+}
+
+static void decode_error_response(const char* hex_string,
+                                  GQuark domain,
+                                  gint code) {
+  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+  g_autoptr(GBytes) message = hex_string_to_bytes(hex_string);
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlMethodResponse) response =
+      fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+  EXPECT_EQ(response, nullptr);
+  EXPECT_TRUE(g_error_matches(error, domain, code));
+}
+
+TEST(FlStandardMethodCodecTest, EncodeMethodCallNullptrArgs) {
+  g_autofree gchar* hex_string = encode_method_call("hello", nullptr);
+  EXPECT_STREQ(hex_string, "070568656c6c6f00");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeMethodCallNullArgs) {
+  g_autoptr(FlValue) value = fl_value_new_null();
+  g_autofree gchar* hex_string = encode_method_call("hello", value);
+  EXPECT_STREQ(hex_string, "070568656c6c6f00");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeMethodCallStringArgs) {
+  g_autoptr(FlValue) args = fl_value_new_string("world");
+  g_autofree gchar* hex_string = encode_method_call("hello", args);
+  EXPECT_STREQ(hex_string, "070568656c6c6f0705776f726c64");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeMethodCallListArgs) {
+  g_autoptr(FlValue) args = fl_value_new_list();
+  fl_value_append_take(args, fl_value_new_string("count"));
+  fl_value_append_take(args, fl_value_new_int(42));
+  g_autofree gchar* hex_string = encode_method_call("hello", args);
+  EXPECT_STREQ(hex_string, "070568656c6c6f0c020705636f756e74032a000000");
+}
+
+TEST(FlStandardMethodCodecTest, DecodeMethodCallNullArgs) {
+  g_autofree gchar* name = nullptr;
+  g_autoptr(FlValue) args = nullptr;
+  decode_method_call("070568656c6c6f00", &name, &args);
+  EXPECT_STREQ(name, "hello");
+  ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_NULL);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeMethodCallStringArgs) {
+  g_autofree gchar* name = nullptr;
+  g_autoptr(FlValue) args = nullptr;
+  decode_method_call("070568656c6c6f0705776f726c64", &name, &args);
+  EXPECT_STREQ(name, "hello");
+  ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_STRING);
+  EXPECT_STREQ(fl_value_get_string(args), "world");
+}
+
+TEST(FlStandardMethodCodecTest, DecodeMethodCallListArgs) {
+  g_autofree gchar* name = nullptr;
+  g_autoptr(FlValue) args = nullptr;
+  decode_method_call("070568656c6c6f0c020705636f756e74032a000000", &name,
+                     &args);
+  EXPECT_STREQ(name, "hello");
+  ASSERT_EQ(fl_value_get_type(args), FL_VALUE_TYPE_LIST);
+  EXPECT_EQ(fl_value_get_length(args), static_cast<size_t>(2));
+
+  FlValue* arg0 = fl_value_get_list_value(args, 0);
+  ASSERT_EQ(fl_value_get_type(arg0), FL_VALUE_TYPE_STRING);
+  EXPECT_STREQ(fl_value_get_string(arg0), "count");
+
+  FlValue* arg1 = fl_value_get_list_value(args, 1);
+  ASSERT_EQ(fl_value_get_type(arg1), FL_VALUE_TYPE_INT);
+  EXPECT_EQ(fl_value_get_int(arg1), 42);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeMethodCallNoData) {
+  decode_error_method_call("", FL_MESSAGE_CODEC_ERROR,
+                           FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeMethodCallNullMethodName) {
+  decode_error_method_call("000000", FL_MESSAGE_CODEC_ERROR,
+                           FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeMethodCallMissingArgs) {
+  decode_error_method_call("070568656c6c6f", FL_MESSAGE_CODEC_ERROR,
+                           FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA);
+}
+
+TEST(FlStandardMethodCodecTest, EncodeSuccessEnvelopeNullptr) {
+  g_autofree gchar* hex_string = encode_success_envelope(nullptr);
+  EXPECT_STREQ(hex_string, "0000");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeSuccessEnvelopeNull) {
+  g_autoptr(FlValue) result = fl_value_new_null();
+  g_autofree gchar* hex_string = encode_success_envelope(result);
+  EXPECT_STREQ(hex_string, "0000");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeSuccessEnvelopeString) {
+  g_autoptr(FlValue) result = fl_value_new_string("hello");
+  g_autofree gchar* hex_string = encode_success_envelope(result);
+  EXPECT_STREQ(hex_string, "00070568656c6c6f");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeSuccessEnvelopeList) {
+  g_autoptr(FlValue) result = fl_value_new_list();
+  fl_value_append_take(result, fl_value_new_string("count"));
+  fl_value_append_take(result, fl_value_new_int(42));
+  g_autofree gchar* hex_string = encode_success_envelope(result);
+  EXPECT_STREQ(hex_string, "000c020705636f756e74032a000000");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeErrorEnvelopeEmptyCode) {
+  g_autofree gchar* hex_string = encode_error_envelope("", nullptr, nullptr);
+  EXPECT_STREQ(hex_string, "0107000000");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeErrorEnvelopeNonMessageOrDetails) {
+  g_autofree gchar* hex_string =
+      encode_error_envelope("error", nullptr, nullptr);
+  EXPECT_STREQ(hex_string, "0107056572726f720000");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeErrorEnvelopeMessage) {
+  g_autofree gchar* hex_string =
+      encode_error_envelope("error", "message", nullptr);
+  EXPECT_STREQ(hex_string, "0107056572726f7207076d65737361676500");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeErrorEnvelopeDetails) {
+  g_autoptr(FlValue) details = fl_value_new_list();
+  fl_value_append_take(details, fl_value_new_string("count"));
+  fl_value_append_take(details, fl_value_new_int(42));
+  g_autofree gchar* hex_string =
+      encode_error_envelope("error", nullptr, details);
+  EXPECT_STREQ(hex_string, "0107056572726f72000c020705636f756e74032a000000");
+}
+
+TEST(FlStandardMethodCodecTest, EncodeErrorEnvelopeMessageAndDetails) {
+  g_autoptr(FlValue) details = fl_value_new_list();
+  fl_value_append_take(details, fl_value_new_string("count"));
+  fl_value_append_take(details, fl_value_new_int(42));
+  g_autofree gchar* hex_string =
+      encode_error_envelope("error", "message", details);
+  EXPECT_STREQ(
+      hex_string,
+      "0107056572726f7207076d6573736167650c020705636f756e74032a000000");
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseSuccessNull) {
+  g_autoptr(FlValue) result = fl_value_new_null();
+  decode_response_with_success("0000", result);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseSuccessString) {
+  g_autoptr(FlValue) result = fl_value_new_string("hello");
+  decode_response_with_success("00070568656c6c6f", result);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseSuccessList) {
+  g_autoptr(FlValue) result = fl_value_new_list();
+  fl_value_append_take(result, fl_value_new_string("count"));
+  fl_value_append_take(result, fl_value_new_int(42));
+  decode_response_with_success("000c020705636f756e74032a000000", result);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorEmptyCode) {
+  decode_response_with_error("0107000000", "", nullptr, nullptr);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorNoMessageOrDetails) {
+  decode_response_with_error("0107056572726f720000", "error", nullptr, nullptr);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorMessage) {
+  decode_response_with_error("0107056572726f7207076d65737361676500", "error",
+                             "message", nullptr);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorDetails) {
+  g_autoptr(FlValue) details = fl_value_new_list();
+  fl_value_append_take(details, fl_value_new_string("count"));
+  fl_value_append_take(details, fl_value_new_int(42));
+  decode_response_with_error("0107056572726f72000c020705636f756e74032a000000",
+                             "error", nullptr, details);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorMessageAndDetails) {
+  g_autoptr(FlValue) details = fl_value_new_list();
+  fl_value_append_take(details, fl_value_new_string("count"));
+  fl_value_append_take(details, fl_value_new_int(42));
+  decode_response_with_error(
+      "0107056572726f7207076d6573736167650c020705636f756e74032a000000", "error",
+      "message", details);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseSuccessNoData) {
+  decode_error_response("00", FL_MESSAGE_CODEC_ERROR,
+                        FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseSuccessExtraData) {
+  decode_error_response("000000", FL_MESSAGE_CODEC_ERROR,
+                        FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorNoData) {
+  decode_error_response("01", FL_MESSAGE_CODEC_ERROR,
+                        FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorMissingMessageAndDetails) {
+  decode_error_response("0107056572726f72", FL_MESSAGE_CODEC_ERROR,
+                        FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorMissingDetails) {
+  decode_error_response("0107056572726f7200", FL_MESSAGE_CODEC_ERROR,
+                        FL_MESSAGE_CODEC_ERROR_OUT_OF_DATA);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseErrorExtraData) {
+  decode_error_response("0107056572726f72000000", FL_MESSAGE_CODEC_ERROR,
+                        FL_MESSAGE_CODEC_ERROR_FAILED);
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseNotImplemented) {
+  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
+  g_autoptr(GBytes) message = g_bytes_new(nullptr, 0);
+  g_autoptr(GError) error = nullptr;
+  g_autoptr(FlMethodResponse) response =
+      fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error);
+  ASSERT_NE(response, nullptr);
+  EXPECT_EQ(error, nullptr);
+  EXPECT_TRUE(FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response));
+}
+
+TEST(FlStandardMethodCodecTest, DecodeResponseUnknownEnvelope) {
+  decode_error_response("02", FL_MESSAGE_CODEC_ERROR,
+                        FL_MESSAGE_CODEC_ERROR_FAILED);
+}
diff --git a/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h b/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h
new file mode 100644
index 0000000..70ddf79
--- /dev/null
+++ b/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h
@@ -0,0 +1,44 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_JSON_METHOD_CODEC_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_JSON_METHOD_CODEC_H_
+
+#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION)
+#error "Only <flutter_linux/flutter_linux.h> can be included directly."
+#endif
+
+#include "fl_method_codec.h"
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE(FlJsonMethodCodec,
+                     fl_json_method_codec,
+                     FL,
+                     JSON_METHOD_CODEC,
+                     FlMethodCodec)
+
+/**
+ * FlJsonMethodCodec:
+ *
+ * #FlJsonMessageCodec is an #FlMethodCodec that implements method calls using
+ * the Flutter JSON message encoding. It should be used with an
+ * #FlMethodChannel.
+ *
+ * #FlJsonMethodCodec matches the JSONMethodCodec class in the Flutter services
+ * library.
+ */
+
+/**
+ * fl_json_method_codec_new:
+ *
+ * Creates an #FlJsonMethodCodec.
+ *
+ * Returns: a new #FlJsonMethodCodec.
+ */
+FlJsonMethodCodec* fl_json_method_codec_new();
+
+G_END_DECLS
+
+#endif  // FLUTTER_SHELL_PLATFORM_LINUX_FL_JSON_METHOD_CODEC_H_
diff --git a/shell/platform/linux/public/flutter_linux/fl_method_call.h b/shell/platform/linux/public/flutter_linux/fl_method_call.h
new file mode 100644
index 0000000..5a5ebc7
--- /dev/null
+++ b/shell/platform/linux/public/flutter_linux/fl_method_call.h
@@ -0,0 +1,115 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CALL_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CALL_H_
+
+#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION)
+#error "Only <flutter_linux/flutter_linux.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+
+#include "fl_method_response.h"
+#include "fl_value.h"
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE(FlMethodCall, fl_method_call, FL, METHOD_CALL, GObject)
+
+/**
+ * FlMethodCall:
+ *
+ * #FlMethodCall represents and incoming method call as returned by an
+ * #FlMethodChannel.
+ */
+
+/**
+ * fl_method_call_get_name:
+ * @method_call: an #FlMethodCall.
+ *
+ * Gets the name of the method call.
+ *
+ * Returns: a method name.
+ */
+const gchar* fl_method_call_get_name(FlMethodCall* method_call);
+
+/**
+ * fl_method_call_get_args:
+ * @method_call: an #FlMethodCall.
+ *
+ * Gets the arguments passed to the method.
+ *
+ * Returns: an #FlValue.
+ */
+FlValue* fl_method_call_get_args(FlMethodCall* method_call);
+
+/**
+ * fl_method_call_respond:
+ * @method_call: an #FlMethodCall.
+ * @response: an #FlMethodResponse.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Responds to a method call.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean fl_method_call_respond(FlMethodCall* method_call,
+                                FlMethodResponse* response,
+                                GError** error);
+
+/**
+ * fl_method_call_respond_success:
+ * @method_call: an #FlMethodCall.
+ * @result: (allow-none): value to respond with, must match what the
+ * #FlMethodCodec supports.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Convenience method that responds to method call with
+ * #FlMethodSuccessResponse.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean fl_method_call_respond_success(FlMethodCall* method_call,
+                                        FlValue* result,
+                                        GError** error);
+
+/**
+ * fl_method_call_respond_error:
+ * @method_call: an #FlMethodCall.
+ * @code: error code.
+ * @message: (allow-none): error message.
+ * @details: (allow-none): details for the error.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Convenience method that responds to method call with #FlMethodErrorResponse.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean fl_method_call_respond_error(FlMethodCall* method_call,
+                                      const gchar* code,
+                                      const gchar* message,
+                                      FlValue* details,
+                                      GError** error);
+
+/**
+ * fl_method_call_respond_not_implemented:
+ * @method_call: an #FlMethodCall.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Convenience method that responds to method call with
+ * #FlMethodNotImplementedResponse.
+ *
+ * Returns: %TRUE on success.
+ */
+gboolean fl_method_call_respond_not_implemented(FlMethodCall* method_call,
+                                                GError** error);
+
+G_END_DECLS
+
+#endif  // FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CALL_H_
diff --git a/shell/platform/linux/public/flutter_linux/fl_method_channel.h b/shell/platform/linux/public/flutter_linux/fl_method_channel.h
new file mode 100644
index 0000000..0ef04ff
--- /dev/null
+++ b/shell/platform/linux/public/flutter_linux/fl_method_channel.h
@@ -0,0 +1,182 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CHANNEL_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CHANNEL_H_
+
+#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION)
+#error "Only <flutter_linux/flutter_linux.h> can be included directly."
+#endif
+
+#include <gio/gio.h>
+#include <glib-object.h>
+
+#include "fl_binary_messenger.h"
+#include "fl_method_call.h"
+#include "fl_method_codec.h"
+#include "fl_method_response.h"
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE(FlMethodChannel,
+                     fl_method_channel,
+                     FL,
+                     METHOD_CHANNEL,
+                     GObject)
+
+/**
+ * FlMethodChannel:
+ *
+ * #FlMethodChannel is an object that allows method calls to and from Dart code.
+ *
+ * The following example shows how to call and handle methods on a channel.
+ * See #FlMethodResponse for how to handle errors in more detail.
+ *
+ * |[<!-- language="C" -->
+ * static FlMethodChannel *channel = NULL;
+ *
+ * static void method_call_cb (FlMethodChannel* channel,
+ *                             FlMethodCall* method_call,
+ *                             gpointer user_data) {
+ *   g_autoptr(FlMethodResponse) response = NULL;
+ *   if (strcmp (fl_method_call_get_name (method_call), "Foo.bar") == 0) {
+ *     g_autoptr(GError) bar_error = NULL;
+ *     g_autoptr(FlValue) result =
+ *         do_bar (fl_method_call_get_args (method_call), &bar_error);
+ *     if (result == NULL)
+ *       response =
+ *         FL_METHOD_RESPONSE (fl_method_error_response_new ("bar error",
+ *                                                           bar_error->message));
+ *     else
+ *       response = FL_METHOD_RESPONSE (fl_method_success_response_new
+ * (result)); } else response = FL_METHOD_RESPONSE
+ * (fl_method_not_implemented_response_new ());
+ *
+ *   g_autoptr(GError) error = NULL;
+ *   if (!fl_method_call_respond(method_call, response))
+ *     g_warning ("Failed to send response: %s", error->message);
+ * }
+ *
+ * static void method_response_cb(GObject *object,
+ *                                GAsyncResult *result,
+ *                                gpointer user_data) {
+ *   g_autoptr(GError) error = NULL;
+ *   g_autoptr(FlMethodResponse) response =
+ *     fl_method_channel_invoke_method_finish (FL_METHOD_CODEC (object), result,
+ *                                             &error);
+ *   if (response == NULL) {
+ *     g_warning ("Failed to call method: %s", error->message);
+ *     return;
+ *   }
+ *
+ *   g_autoptr(FlValue) value =
+ *     fl_method_response_get_result (response, &error);
+ *   if (response == NULL) {
+ *     g_warning ("Method returned error: %s", error->message);
+ *     return;
+ *   }
+ *
+ *   use_result (value);
+ * }
+ *
+ * static void call_method () {
+ *   g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new ();
+ *   channel =
+ *     fl_method_channel_new(messenger, "flutter/foo", FL_METHOD_CODEC (codec));
+ *   fl_method_channel_set_method_call_handler (channel, method_call_cb, NULL);
+ *
+ *   g_autoptr(FlValue) args = fl_value_new_string ("Hello World");
+ *   fl_method_channel_invoke_method (channel, "Foo.foo", args,
+ *                                    cancellable, method_response_cb, NULL);
+ * }
+ * ]|
+ *
+ * #FlMethodChannel matches the MethodChannel class in the Flutter services
+ * library.
+ */
+
+/**
+ * FlMethodChannelMethodCallHandler:
+ * @channel: an #FlMethodChannel.
+ * @method_call: an #FlMethodCall.
+ * @user_data: (closure): data provided when registering this handler.
+ *
+ * Function called when a method call is received. Respond to the method call
+ * with fl_method_call_respond(). If the response is not occurring in this
+ * callback take a reference to @method_call and release that once it has been
+ * responded to.Failing to respond before the last reference to @method_call is
+ * dropped is a programming error.
+ */
+typedef void (*FlMethodChannelMethodCallHandler)(FlMethodChannel* channel,
+                                                 FlMethodCall* method_call,
+                                                 gpointer user_data);
+
+/**
+ * fl_method_channel_new:
+ * @messenger: an #FlBinaryMessenger.
+ * @name: a channel name.
+ * @codec: the method codec.
+ *
+ * Creates a new method channel. @codec must match the codec used on the Dart
+ * end of the channel.
+ *
+ * Returns: a new #FlMethodChannel.
+ */
+FlMethodChannel* fl_method_channel_new(FlBinaryMessenger* messenger,
+                                       const gchar* name,
+                                       FlMethodCodec* codec);
+
+/**
+ * fl_method_channel_set_method_call_handler:
+ * @channel: an #FlMethodChannel.
+ * @handler: function to call when a method call is received on this channel.
+ * @user_data: (closure): user data to pass to @handler.
+ *
+ * Sets the function called when a method is called. Call
+ * fl_method_call_respond() when the method completes.
+ */
+void fl_method_channel_set_method_call_handler(
+    FlMethodChannel* channel,
+    FlMethodChannelMethodCallHandler handler,
+    gpointer user_data);
+
+/**
+ * fl_method_channel_invoke_method:
+ * @channel: an #FlMethodChannel.
+ * @method: the method to call.
+ * @args: (allow-none): arguments to the method, must match what the
+ * #FlMethodCodec supports.
+ * @cancellable: (allow-none): a #GCancellable or %NULL.
+ * @callback: (scope async): (allow-none): a #GAsyncReadyCallback to call when
+ * the request is satisfied or %NULL to ignore the response.
+ * @user_data: (closure): user data to pass to @callback.
+ *
+ * Calls a method on this channel.
+ */
+void fl_method_channel_invoke_method(FlMethodChannel* channel,
+                                     const gchar* method,
+                                     FlValue* args,
+                                     GCancellable* cancellable,
+                                     GAsyncReadyCallback callback,
+                                     gpointer user_data);
+
+/**
+ * fl_method_channel_invoke_method_finish:
+ * @channel: an #FlMethodChannel.
+ * @result:  #GAsyncResult.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Completes request started with fl_method_channel_invoke_method().
+ *
+ * Returns: (transfer full): an #FlMethodResponse or %NULL on error.
+ */
+FlMethodResponse* fl_method_channel_invoke_method_finish(
+    FlMethodChannel* channel,
+    GAsyncResult* result,
+    GError** error);
+
+G_END_DECLS
+
+#endif  // FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CHANNEL_H_
diff --git a/shell/platform/linux/public/flutter_linux/fl_method_codec.h b/shell/platform/linux/public/flutter_linux/fl_method_codec.h
new file mode 100644
index 0000000..97bf8e9
--- /dev/null
+++ b/shell/platform/linux/public/flutter_linux/fl_method_codec.h
@@ -0,0 +1,131 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CODEC_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CODEC_H_
+
+#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION)
+#error "Only <flutter_linux/flutter_linux.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+
+#include "fl_method_response.h"
+#include "fl_value.h"
+
+G_BEGIN_DECLS
+
+G_DECLARE_DERIVABLE_TYPE(FlMethodCodec,
+                         fl_method_codec,
+                         FL,
+                         METHOD_CODEC,
+                         GObject)
+
+/**
+ * FlMethodCodec:
+ *
+ * #FlMethodCodec is an abstract class that encodes and decodes method calls on
+ * a platform channel. Override this class to implement an encoding.
+ *
+ * #FlMethodCodec matches the MethodCodec class in the Flutter services
+ * library.
+ */
+
+struct _FlMethodCodecClass {
+  GObjectClass parent_class;
+
+  /**
+   * FlMethodCodec::encode_method_call:
+   * @codec: an #FlMethodCodec.
+   * @name: method name.
+   * @args: (allow-none): method arguments, or %NULL.
+   * @error: (allow-none): #GError location to store the error occurring, or
+   * %NULL.
+   *
+   * Encodes a method call.
+   *
+   * Returns: (transfer full): a binary encoding of this method call or %NULL if
+   * not able to encode.
+   */
+  GBytes* (*encode_method_call)(FlMethodCodec* codec,
+                                const gchar* name,
+                                FlValue* args,
+                                GError** error);
+
+  /**
+   * FlMethodCodec::decode_method_call:
+   * @codec: an #FlMethodCodec
+   * @message: message to decode.
+   * @name: (transfer full): location to write method name or %NULL if not
+   * required
+   * @args: (transfer full): location to write method arguments, or %NULL if not
+   * required
+   * @error: (allow-none): #GError location to store the error occurring, or
+   * %NULL
+   *
+   * Decodes a method call.
+   *
+   * Returns: %TRUE if successfully decoded.
+   */
+  gboolean (*decode_method_call)(FlMethodCodec* codec,
+                                 GBytes* message,
+                                 gchar** name,
+                                 FlValue** args,
+                                 GError** error);
+
+  /**
+   * FlMethodCodec::encode_success_envelope:
+   * @codec: an #FlMethodCodec.
+   * @result: (allow-none): method result, or %NULL.
+   * @error: (allow-none): #GError location to store the error occurring, or
+   * %NULL.
+   *
+   * Encodes a successful response to a method call.
+   *
+   * Returns: (transfer full): a binary encoding of this response or %NULL if
+   * not able to encode.
+   */
+  GBytes* (*encode_success_envelope)(FlMethodCodec* codec,
+                                     FlValue* result,
+                                     GError** error);
+
+  /**
+   * FlMethodCodec::encode_error_envelope:
+   * @codec: an #FlMethodCodec.
+   * @code: an error code.
+   * @message: (allow-none): an error message, or %NULL.
+   * @details: (allow-none): error details, or %NULL.
+   * @error: (allow-none): #GError location to store the error occurring, or
+   * %NULL.
+   *
+   * Encodes an error response to a method call.
+   *
+   * Returns: (transfer full): a binary encoding of this response or %NULL if
+   * not able to encode.
+   */
+  GBytes* (*encode_error_envelope)(FlMethodCodec* codec,
+                                   const gchar* code,
+                                   const gchar* message,
+                                   FlValue* details,
+                                   GError** error);
+
+  /**
+   * FlMethodCodec::decode_response:
+   * @codec: an #FlMethodCodec.
+   * @message: message to decode.
+   * @error: (allow-none): #GError location to store the error occurring, or
+   * %NULL.
+   *
+   * Decodes a response to a method call.
+   *
+   * Returns: a new #FlMethodResponse or %NULL on error.
+   */
+  FlMethodResponse* (*decode_response)(FlMethodCodec* codec,
+                                       GBytes* message,
+                                       GError** error);
+};
+
+G_END_DECLS
+
+#endif  // FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_CODEC_H_
diff --git a/shell/platform/linux/public/flutter_linux/fl_method_response.h b/shell/platform/linux/public/flutter_linux/fl_method_response.h
new file mode 100644
index 0000000..949cddc
--- /dev/null
+++ b/shell/platform/linux/public/flutter_linux/fl_method_response.h
@@ -0,0 +1,212 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_RESPONSE_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_RESPONSE_H_
+
+#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION)
+#error "Only <flutter_linux/flutter_linux.h> can be included directly."
+#endif
+
+#include <glib-object.h>
+
+#include "fl_value.h"
+
+G_BEGIN_DECLS
+
+/**
+ * FlMethodResponseError:
+ * @FL_METHOD_RESPONSE_ERROR_FAILED: Call failed due to an unspecified error.
+ * @FL_METHOD_RESPONSE_ERROR_REMOTE_ERROR: An error was returned by the other
+ * side of the channel.
+ * @FL_METHOD_RESPONSE_ERROR_NOT_IMPLEMENTED: The requested method is not
+ * implemented.
+ *
+ * Errors set by `fl_method_response_get_result` when the method call response
+ * is not #FlMethodSuccessResponse.
+ */
+#define FL_METHOD_RESPONSE_ERROR fl_method_response_error_quark()
+
+typedef enum {
+  FL_METHOD_RESPONSE_ERROR_FAILED,
+  FL_METHOD_RESPONSE_ERROR_REMOTE_ERROR,
+  FL_METHOD_RESPONSE_ERROR_NOT_IMPLEMENTED,
+} FlMethodResponseError;
+
+GQuark fl_method_response_error_quark(void) G_GNUC_CONST;
+
+G_DECLARE_DERIVABLE_TYPE(FlMethodResponse,
+                         fl_method_response,
+                         FL,
+                         METHOD_RESPONSE,
+                         GObject)
+
+struct _FlMethodResponseClass {
+  GObjectClass parent_class;
+};
+
+G_DECLARE_FINAL_TYPE(FlMethodSuccessResponse,
+                     fl_method_success_response,
+                     FL,
+                     METHOD_SUCCESS_RESPONSE,
+                     FlMethodResponse)
+
+G_DECLARE_FINAL_TYPE(FlMethodErrorResponse,
+                     fl_method_error_response,
+                     FL,
+                     METHOD_ERROR_RESPONSE,
+                     FlMethodResponse)
+
+G_DECLARE_FINAL_TYPE(FlMethodNotImplementedResponse,
+                     fl_method_not_implemented_response,
+                     FL,
+                     METHOD_NOT_IMPLEMENTED_RESPONSE,
+                     FlMethodResponse)
+
+/**
+ * FlMethodResponse:
+ *
+ * #FlMethodResponse contains the information returned when an #FlMethodChannel
+ * method call returns. If you expect the method call to be successful use
+ * fl_method_response_get_result(). If you want to handle error cases then you
+ * should use code like:
+ *
+ * |[<!-- language="C" -->
+ *   if (FL_IS_METHOD_SUCCESS_RESPONSE (response)) {
+ *     FlValue *result =
+ *       fl_method_success_response_get_result(
+ *         FL_METHOD_SUCCESS_RESPONSE (response));
+ *     handle_result (result);
+ *   } else if (FL_IS_METHOD_ERROR_RESPONSE (response)) {
+ *     FlMethodErrorResponse *error_response =
+ *       FL_METHOD_ERROR_RESPONSE (response);
+ *     handle_error (fl_method_error_response_get_code (error_response),
+ *                   fl_method_error_response_get_message (error_response),
+ *                   fl_method_error_response_get_details (error_response));
+ *   }
+ *   else if (FL_IS_METHOD_ERROR_RESPONSE (response)) {
+ *     handle_not_implemented ();
+ *   }
+ * }
+ * ]|
+ */
+
+/**
+ * FlMethodSuccessResponse:
+ *
+ * #FlMethodSuccessResponse is the #FlMethodResponse returned when a method call
+ * has successfully completed. The result of the method call is obtained using
+ * `fl_method_success_response_get_result`.
+ */
+
+/**
+ * FlMethodErrorResponse:
+ *
+ * #FlMethodErrorResponse is the #FlMethodResponse returned when a method call
+ * results in an error. The error details are obtained using
+ * `fl_method_error_response_get_code`, `fl_method_error_response_get_message`
+ * and `fl_method_error_response_get_details`.
+ */
+
+/**
+ * FlMethodNotImplementedResponse:
+ *
+ * #FlMethodNotImplementedResponse is the #FlMethodResponse returned when a
+ * method call is not implemented.
+ */
+
+/**
+ * fl_method_response_get_result:
+ * @response: an #FlMethodResponse.
+ * @error: (allow-none): #GError location to store the error occurring, or %NULL
+ * to ignore.
+ *
+ * Gets the result of a method call, or an error if the response wasn't
+ * successful.
+ *
+ * Returns: an #FlValue or %NULL on error.
+ */
+FlValue* fl_method_response_get_result(FlMethodResponse* response,
+                                       GError** error);
+
+/**
+ * fl_method_success_response_new:
+ * @result: (allow-none): the #FlValue returned by the method call or %NULL.
+ *
+ * Creates a response to a method call when that method has successfully
+ * completed.
+ *
+ * Returns: a new #FlMethodResponse.
+ */
+FlMethodSuccessResponse* fl_method_success_response_new(FlValue* result);
+
+/**
+ * fl_method_success_response_get_result:
+ * @response: an #FlMethodSuccessResponse.
+ *
+ * Gets the result of the method call.
+ *
+ * Returns: an #FlValue.
+ */
+FlValue* fl_method_success_response_get_result(
+    FlMethodSuccessResponse* response);
+
+/**
+ * fl_method_error_response_new:
+ * @result: an #FlValue.
+ * @code: an error code.
+ * @message: (allow-none): an error message.
+ * @details: (allow-none): error details.
+ *
+ * Creates a response to a method call when that method has returned an error.
+ *
+ * Returns: a new #FlMethodErrorResponse.
+ */
+FlMethodErrorResponse* fl_method_error_response_new(const gchar* code,
+                                                    const gchar* message,
+                                                    FlValue* details);
+
+/**
+ * fl_method_error_response_get_code:
+ * @response: an #FlMethodErrorResponse.
+ *
+ * Gets the error code reported.
+ *
+ * Returns: an error code.
+ */
+const gchar* fl_method_error_response_get_code(FlMethodErrorResponse* response);
+
+/**
+ * fl_method_error_response_get_message:
+ * @response: an #FlMethodErrorResponse.
+ *
+ * Gets the error message reported.
+ *
+ * Returns: an error message or %NULL if no error message provided.
+ */
+const gchar* fl_method_error_response_get_message(
+    FlMethodErrorResponse* response);
+
+/**
+ * fl_method_error_response_get_details:
+ * @response: an #FlMethodErrorResponse.
+ *
+ * Gets the details provided with this error.
+ *
+ * Returns: an #FlValue or %NULL if no details provided.
+ */
+FlValue* fl_method_error_response_get_details(FlMethodErrorResponse* response);
+
+/**
+ * fl_method_not_implemented_response_new:
+ *
+ * Creates a response to a method call when that method does not exist.
+ *
+ * Returns: a new #FlMethodNotImplementedResponse.
+ */
+FlMethodNotImplementedResponse* fl_method_not_implemented_response_new();
+
+G_END_DECLS
+
+#endif  // FLUTTER_SHELL_PLATFORM_LINUX_FL_METHOD_RESPONSE_H_
diff --git a/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h b/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h
new file mode 100644
index 0000000..1685ebc
--- /dev/null
+++ b/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h
@@ -0,0 +1,44 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_STANDARD_METHOD_CODEC_H_
+#define FLUTTER_SHELL_PLATFORM_LINUX_FL_STANDARD_METHOD_CODEC_H_
+
+#if !defined(__FLUTTER_LINUX_INSIDE__) && !defined(FLUTTER_LINUX_COMPILATION)
+#error "Only <flutter_linux/flutter_linux.h> can be included directly."
+#endif
+
+#include "fl_method_codec.h"
+
+G_BEGIN_DECLS
+
+G_DECLARE_FINAL_TYPE(FlStandardMethodCodec,
+                     fl_standard_method_codec,
+                     FL,
+                     STANDARD_METHOD_CODEC,
+                     FlMethodCodec)
+
+/**
+ * FlStandardMethodCodec:
+ *
+ * #FlStandardMethodCodec is an #FlMethodCodec that implements method calls
+ * using the Flutter standard message encoding. It should be used with a
+ * #FlMethodChannel.
+ *
+ * #FlStandardMethodCodec matches the StandardMethodCodec class in the Flutter
+ * services library.
+ */
+
+/**
+ * fl_standard_method_codec_new:
+ *
+ * Creates an #FlStandardMethodCodec.
+ *
+ * Returns: a new #FlStandardMethodCodec.
+ */
+FlStandardMethodCodec* fl_standard_method_codec_new();
+
+G_END_DECLS
+
+#endif  // FLUTTER_SHELL_PLATFORM_LINUX_FL_STANDARD_METHOD_CODEC_H_
diff --git a/shell/platform/linux/public/flutter_linux/flutter_linux.h b/shell/platform/linux/public/flutter_linux/flutter_linux.h
index b26e9ab..03f9dfe 100644
--- a/shell/platform/linux/public/flutter_linux/flutter_linux.h
+++ b/shell/platform/linux/public/flutter_linux/flutter_linux.h
@@ -13,8 +13,14 @@
 #include <flutter_linux/fl_dart_project.h>
 #include <flutter_linux/fl_engine.h>
 #include <flutter_linux/fl_json_message_codec.h>
+#include <flutter_linux/fl_json_method_codec.h>
 #include <flutter_linux/fl_message_codec.h>
+#include <flutter_linux/fl_method_call.h>
+#include <flutter_linux/fl_method_channel.h>
+#include <flutter_linux/fl_method_codec.h>
+#include <flutter_linux/fl_method_response.h>
 #include <flutter_linux/fl_standard_message_codec.h>
+#include <flutter_linux/fl_standard_method_codec.h>
 #include <flutter_linux/fl_string_codec.h>
 #include <flutter_linux/fl_value.h>
 #include <flutter_linux/fl_view.h>