1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | import 'package:flutter/foundation.dart'; |
6 | |
7 | import 'platform_channel.dart'; |
8 | |
9 | export 'dart:typed_data' show ByteData; |
10 | |
11 | /// A message encoding/decoding mechanism. |
12 | /// |
13 | /// Both operations throw an exception, if conversion fails. Such situations |
14 | /// should be treated as programming errors. |
15 | /// |
16 | /// See also: |
17 | /// |
18 | /// * [BasicMessageChannel], which use [MessageCodec]s for communication |
19 | /// between Flutter and platform plugins. |
20 | abstract class MessageCodec<T> { |
21 | /// Encodes the specified [message] in binary. |
22 | /// |
23 | /// Returns null if the message is null. |
24 | ByteData? encodeMessage(T message); |
25 | |
26 | /// Decodes the specified [message] from binary. |
27 | /// |
28 | /// Returns null if the message is null. |
29 | T? decodeMessage(ByteData? message); |
30 | } |
31 | |
32 | /// A command object representing the invocation of a named method. |
33 | @pragma('vm:keep-name' ) |
34 | @immutable |
35 | class MethodCall { |
36 | /// Creates a [MethodCall] representing the invocation of [method] with the |
37 | /// specified [arguments]. |
38 | const MethodCall(this.method, [this.arguments]); |
39 | |
40 | /// The name of the method to be called. |
41 | final String method; |
42 | |
43 | /// The arguments for the method. |
44 | /// |
45 | /// Must be a valid value for the [MethodCodec] used. |
46 | /// |
47 | /// This property is `dynamic`, which means type-checking is skipped when accessing |
48 | /// this property. To minimize the risk of type errors at runtime, the value should |
49 | /// be cast to `Object?` when accessed. |
50 | final dynamic arguments; |
51 | |
52 | @override |
53 | String toString() => ' ${objectRuntimeType(this, 'MethodCall' )}( $method, $arguments)' ; |
54 | } |
55 | |
56 | /// A codec for method calls and enveloped results. |
57 | /// |
58 | /// All operations throw an exception, if conversion fails. |
59 | /// |
60 | /// See also: |
61 | /// |
62 | /// * [MethodChannel], which use [MethodCodec]s for communication |
63 | /// between Flutter and platform plugins. |
64 | /// * [EventChannel], which use [MethodCodec]s for communication |
65 | /// between Flutter and platform plugins. |
66 | abstract class MethodCodec { |
67 | /// Encodes the specified [methodCall] into binary. |
68 | ByteData encodeMethodCall(MethodCall methodCall); |
69 | |
70 | /// Decodes the specified [methodCall] from binary. |
71 | MethodCall decodeMethodCall(ByteData? methodCall); |
72 | |
73 | /// Decodes the specified result [envelope] from binary. |
74 | /// |
75 | /// Throws [PlatformException], if [envelope] represents an error, otherwise |
76 | /// returns the enveloped result. |
77 | /// |
78 | /// The type returned from [decodeEnvelope] is `dynamic` (not `Object?`), |
79 | /// which means *no type checking is performed on its return value*. It is |
80 | /// strongly recommended that the return value be immediately cast to a known |
81 | /// type to prevent runtime errors due to typos that the type checker could |
82 | /// otherwise catch. |
83 | dynamic decodeEnvelope(ByteData envelope); |
84 | |
85 | /// Encodes a successful [result] into a binary envelope. |
86 | ByteData encodeSuccessEnvelope(Object? result); |
87 | |
88 | /// Encodes an error result into a binary envelope. |
89 | /// |
90 | /// The specified error [code], human-readable error [message] and error |
91 | /// [details] correspond to the fields of [PlatformException]. |
92 | ByteData encodeErrorEnvelope({required String code, String? message, Object? details}); |
93 | } |
94 | |
95 | /// Thrown to indicate that a platform interaction failed in the platform |
96 | /// plugin. |
97 | /// |
98 | /// See also: |
99 | /// |
100 | /// * [MethodCodec], which throws a [PlatformException], if a received result |
101 | /// envelope represents an error. |
102 | /// * [MethodChannel.invokeMethod], which completes the returned future |
103 | /// with a [PlatformException], if invoking the platform plugin method |
104 | /// results in an error envelope. |
105 | /// * [EventChannel.receiveBroadcastStream], which emits |
106 | /// [PlatformException]s as error events, whenever an event received from the |
107 | /// platform plugin is wrapped in an error envelope. |
108 | class PlatformException implements Exception { |
109 | /// Creates a [PlatformException] with the specified error [code] and optional |
110 | /// [message], and with the optional error [details] which must be a valid |
111 | /// value for the [MethodCodec] involved in the interaction. |
112 | PlatformException({required this.code, this.message, this.details, this.stacktrace}); |
113 | |
114 | /// An error code. |
115 | final String code; |
116 | |
117 | /// A human-readable error message, possibly null. |
118 | final String? message; |
119 | |
120 | /// Error details, possibly null. |
121 | /// |
122 | /// This property is `dynamic`, which means type-checking is skipped when accessing |
123 | /// this property. To minimize the risk of type errors at runtime, the value should |
124 | /// be cast to `Object?` when accessed. |
125 | final dynamic details; |
126 | |
127 | /// Native stacktrace for the error, possibly null. |
128 | /// |
129 | /// This contains the native platform stack trace, not the Dart stack trace. |
130 | /// |
131 | /// The stack trace for Dart exceptions can be obtained using try-catch blocks, for example: |
132 | /// |
133 | /// ```dart |
134 | /// try { |
135 | /// // ... |
136 | /// } catch (e, stacktrace) { |
137 | /// print(stacktrace); |
138 | /// } |
139 | /// ``` |
140 | /// |
141 | /// On Android this field is populated when a `RuntimeException` or a subclass of it is thrown in the method call handler, |
142 | /// as shown in the following example: |
143 | /// |
144 | /// ```kotlin |
145 | /// import androidx.annotation.NonNull |
146 | /// import io.flutter.embedding.android.FlutterActivity |
147 | /// import io.flutter.embedding.engine.FlutterEngine |
148 | /// import io.flutter.plugin.common.MethodChannel |
149 | /// |
150 | /// class MainActivity: FlutterActivity() { |
151 | /// private val CHANNEL = "channel_name" |
152 | /// |
153 | /// override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { |
154 | /// super.configureFlutterEngine(flutterEngine) |
155 | /// MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { |
156 | /// call, result -> throw RuntimeException("Oh no") |
157 | /// } |
158 | /// } |
159 | /// } |
160 | /// ``` |
161 | /// |
162 | /// It is also populated on Android if the method channel result is not serializable. |
163 | /// If the result is not serializable, an exception gets thrown during the serialization process. |
164 | /// This can be seen in the following example: |
165 | /// |
166 | /// ```kotlin |
167 | /// import androidx.annotation.NonNull |
168 | /// import io.flutter.embedding.android.FlutterActivity |
169 | /// import io.flutter.embedding.engine.FlutterEngine |
170 | /// import io.flutter.plugin.common.MethodChannel |
171 | /// |
172 | /// class MainActivity: FlutterActivity() { |
173 | /// private val CHANNEL = "channel_name" |
174 | /// |
175 | /// override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { |
176 | /// super.configureFlutterEngine(flutterEngine) |
177 | /// MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { |
178 | /// call, result -> result.success(Object()) |
179 | /// } |
180 | /// } |
181 | /// } |
182 | /// ``` |
183 | /// |
184 | /// In the cases described above, the content of [stacktrace] will be the unprocessed output of calling `toString()` on the exception. |
185 | /// |
186 | /// MacOS, iOS, Linux and Windows don't support querying the native stacktrace. |
187 | /// |
188 | /// On custom Flutter embedders this value will be null on platforms that don't support querying the call stack. |
189 | final String? stacktrace; |
190 | |
191 | @override |
192 | String toString() => 'PlatformException( $code, $message, $details, $stacktrace)' ; |
193 | } |
194 | |
195 | /// Thrown to indicate that a platform interaction failed to find a handling |
196 | /// plugin. |
197 | /// |
198 | /// See also: |
199 | /// |
200 | /// * [MethodChannel.invokeMethod], which completes the returned future |
201 | /// with a [MissingPluginException], if no plugin handler for the method call |
202 | /// was found. |
203 | /// * [OptionalMethodChannel.invokeMethod], which completes the returned future |
204 | /// with null, if no plugin handler for the method call was found. |
205 | class MissingPluginException implements Exception { |
206 | /// Creates a [MissingPluginException] with an optional human-readable |
207 | /// error message. |
208 | MissingPluginException([this.message]); |
209 | |
210 | /// A human-readable error message, possibly null. |
211 | final String? message; |
212 | |
213 | @override |
214 | String toString() => 'MissingPluginException( $message)' ; |
215 | } |
216 | |