Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
8d923bf9
Unverified
Commit
8d923bf9
authored
Oct 06, 2020
by
Ian Hickson
Committed by
GitHub
Oct 06, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
flutter_web_plugins cleanup and documentation (#67164)
parent
d4112424
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
205 additions
and
57 deletions
+205
-57
flutter_web_plugins.dart
packages/flutter_web_plugins/lib/flutter_web_plugins.dart
+13
-0
plugin_event_channel.dart
...ges/flutter_web_plugins/lib/src/plugin_event_channel.dart
+67
-26
plugin_registry.dart
packages/flutter_web_plugins/lib/src/plugin_registry.dart
+51
-27
plugin_event_channel_test.dart
...s/flutter_web_plugins/test/plugin_event_channel_test.dart
+74
-4
No files found.
packages/flutter_web_plugins/lib/flutter_web_plugins.dart
View file @
8d923bf9
...
...
@@ -4,5 +4,18 @@
// @dart = 2.8
/// The platform channels and plugin registry implementations for
/// the web implementations of Flutter plugins.
///
/// This library provides the [Registrar] class, which is used in the
/// `registerWith` method that is itself called by the code generated
/// by the `flutter` tool for web applications.
///
/// See also:
///
/// * [How to Write a Flutter Web Plugin](https://medium.com/flutter/how-to-write-a-flutter-web-plugin-5e26c689ea1), a Medium article
/// describing how the [url_launcher] package was created using [flutter_web_plugins].
library
flutter_web_plugins
;
export
'src/plugin_event_channel.dart'
;
export
'src/plugin_registry.dart'
;
packages/flutter_web_plugins/lib/src/plugin_event_channel.dart
View file @
8d923bf9
...
...
@@ -21,15 +21,32 @@ import 'plugin_registry.dart';
/// [StandardMethodCodec] is used. If no [binaryMessenger] is provided, then
/// [pluginBinaryMessenger], which sends messages to the framework-side,
/// is used.
///
/// Channels created using this class implement two methods for
/// subscribing to the event stream. The methods use the encoding of
/// the specified [codec].
///
/// The first method is `listen`. When called, it begins forwarding
/// messages to the framework side when they are added to the
/// [controller]. This triggers the [onListen] callback on the
/// [controller].
///
/// The other method is `cancel`. When called, it stops forwarding
/// events to the framework. This triggers the [onCancel] callback on
/// the [controller].
///
/// Events added to the [controller] when the framework is not
/// subscribed are silently discarded.
class
PluginEventChannel
<
T
>
{
/// Creates a new plugin event channel.
///
/// The [name] and [codec] arguments must not be null.
const
PluginEventChannel
(
this
.
name
,
[
this
.
codec
=
const
StandardMethodCodec
(),
BinaryMessenger
binaryMessenger
,
this
.
binaryMessenger
,
])
:
assert
(
name
!=
null
),
assert
(
codec
!=
null
),
_binaryMessenger
=
binaryMessenger
;
assert
(
codec
!=
null
);
/// The logical channel on which communication happens.
///
...
...
@@ -43,28 +60,52 @@ class PluginEventChannel<T> {
/// The messenger used by this channel to send platform messages.
///
/// This must not be null. If not provided, defaults to
/// [pluginBinaryMessenger], which sends messages from the platform-side
/// to the framework-side.
BinaryMessenger
get
binaryMessenger
=>
_binaryMessenger
??
pluginBinaryMessenger
;
final
BinaryMessenger
_binaryMessenger
;
/// Set the stream controller for this event channel.
/// When this is null, the [pluginBinaryMessenger] is used instead,
/// which sends messages from the platform-side to the
/// framework-side.
final
BinaryMessenger
binaryMessenger
;
/// Use [setController] instead.
///
/// This setter is deprecated because it has no corresponding getter,
/// and providing a getter would require making this class non-const.
@Deprecated
(
'Replace calls to the "controller" setter with calls to the "setController" method. '
'This feature was deprecated after v1.23.0-7.0.pre.'
)
set
controller
(
StreamController
<
T
>
controller
)
{
final
_EventChannelHandler
<
T
>
handler
=
_EventChannelHandler
<
T
>(
name
,
codec
,
controller
,
binaryMessenger
,
);
binaryMessenger
.
setMessageHandler
(
name
,
controller
==
null
?
null
:
handler
.
handle
);
setController
(
controller
);
}
/// Changes the stream controller for this event channel.
///
/// Setting the controller to null disconnects from the channel (setting
/// the message handler on the [binaryMessenger] to null).
void
setController
(
StreamController
<
T
>
controller
)
{
final
BinaryMessenger
messenger
=
binaryMessenger
??
pluginBinaryMessenger
;
if
(
controller
==
null
)
{
messenger
.
setMessageHandler
(
name
,
null
);
}
else
{
// The handler object is kept alive via its handle() method
// keeping a reference to itself. Ideally we would keep a
// reference to it so that there was a clear ownership model,
// but that would require making this class non-const. Having
// this class be const is convenient since it allows references
// to be obtained by using the constructor rather than having
// to literally pass references around.
final
_EventChannelHandler
<
T
>
handler
=
_EventChannelHandler
<
T
>(
name
,
codec
,
controller
,
messenger
,
);
messenger
.
setMessageHandler
(
name
,
handler
.
handle
);
}
}
}
class
_EventChannelHandler
<
T
>
{
_EventChannelHandler
(
this
.
name
,
this
.
codec
,
this
.
controller
,
this
.
messenger
);
_EventChannelHandler
(
this
.
name
,
this
.
codec
,
this
.
controller
,
this
.
messenger
)
:
assert
(
messenger
!=
null
)
;
final
String
name
;
final
MethodCodec
codec
;
...
...
@@ -77,14 +118,15 @@ class _EventChannelHandler<T> {
final
MethodCall
call
=
codec
.
decodeMethodCall
(
message
);
switch
(
call
.
method
)
{
case
'listen'
:
assert
(
call
.
arguments
==
null
);
return
_listen
();
case
'cancel'
:
assert
(
call
.
arguments
==
null
);
return
_cancel
();
}
return
null
;
}
// TODO(hterkelsen): Support arguments.
Future
<
ByteData
>
_listen
()
async
{
if
(
subscription
!=
null
)
{
await
subscription
.
cancel
();
...
...
@@ -92,18 +134,17 @@ class _EventChannelHandler<T> {
subscription
=
controller
.
stream
.
listen
((
dynamic
event
)
{
messenger
.
send
(
name
,
codec
.
encodeSuccessEnvelope
(
event
));
},
onError:
(
dynamic
error
)
{
messenger
.
send
(
name
,
codec
.
encodeErrorEnvelope
(
code:
'error'
,
message:
error
.
toString
()));
messenger
.
send
(
name
,
codec
.
encodeErrorEnvelope
(
code:
'error'
,
message:
'
$error
'
));
});
return
codec
.
encodeSuccessEnvelope
(
null
);
}
// TODO(hterkelsen): Support arguments.
Future
<
ByteData
>
_cancel
()
async
{
if
(
subscription
==
null
)
{
return
codec
.
encodeErrorEnvelope
(
code:
'error'
,
message:
'No active stream to cancel.'
);
code:
'error'
,
message:
'No active subscription to cancel.'
,
);
}
await
subscription
.
cancel
();
subscription
=
null
;
...
...
packages/flutter_web_plugins/lib/src/plugin_registry.dart
View file @
8d923bf9
...
...
@@ -13,8 +13,13 @@ import 'package:flutter/services.dart';
typedef
_MessageHandler
=
Future
<
ByteData
>
Function
(
ByteData
);
/// This class registers web platform plugins.
///
/// An instance of this class is available as [webPluginRegistry].
class
PluginRegistry
{
/// Creates a plugin registry.
///
/// The argument selects the [BinaryMessenger] to use. An
/// appropriate value would be [pluginBinaryMessenger].
PluginRegistry
(
this
.
_binaryMessenger
);
final
BinaryMessenger
_binaryMessenger
;
...
...
@@ -25,6 +30,17 @@ class PluginRegistry {
/// Registers this plugin handler with the engine, so that unrecognized
/// platform messages are forwarded to the registry, where they can be
/// correctly dispatched to one of the registered plugins.
///
/// Code generated by the `flutter` tool automatically calls this method
/// for the global [webPluginRegistry] at startup.
///
/// Only one [PluginRegistry] can be registered at a time. Calling this
/// method a second time silently unregisters the first [PluginRegistry]
/// and replaces it with the new one.
///
/// This method uses a function called `webOnlySetPluginHandler` in
/// the [dart:ui] library. That function is only available when
/// compiling for the web.
void
registerMessageHandler
()
{
// The function below is only defined in the Web dart:ui.
// ignore: undefined_function
...
...
@@ -46,22 +62,26 @@ class Registrar {
/// Use this [BinaryMessenger] when creating platform channels in order for
/// them to receive messages from the platform side. For example:
///
///
/// class MyPlugin {
/// static void registerWith(Registrar registrar) {
/// final MethodChannel channel = MethodChannel(
/// 'com.my_plugin/my_plugin',
/// const StandardMethodCodec(),
/// registrar.messenger);
/// final MyPlugin instance = MyPlugin();
/// channel.setMethodCallHandler(instance.handleMethodCall);
/// }
/// ...
/// }
/// ```dart
/// class MyPlugin {
/// static void registerWith(Registrar registrar) {
/// final MethodChannel channel = MethodChannel(
/// 'com.my_plugin/my_plugin',
/// const StandardMethodCodec(),
/// registrar.messenger,
/// );
/// final MyPlugin instance = MyPlugin();
/// channel.setMethodCallHandler(instance.handleMethodCall);
/// }
/// // ...
/// }
/// ```
final
BinaryMessenger
messenger
;
}
/// The default plugin registry for the web.
///
/// Uses [pluginBinaryMessenger] as the [BinaryMessenger].
final
PluginRegistry
webPluginRegistry
=
PluginRegistry
(
pluginBinaryMessenger
);
/// A [BinaryMessenger] which does the inverse of the default framework
...
...
@@ -75,23 +95,23 @@ class _PlatformBinaryMessenger extends BinaryMessenger {
/// Receives a platform message from the framework.
@override
Future
<
void
>
handlePlatformMessage
(
String
channel
,
ByteData
data
,
ui
.
PlatformMessageResponseCallback
callback
)
async
{
Future
<
void
>
handlePlatformMessage
(
String
channel
,
ByteData
data
,
ui
.
PlatformMessageResponseCallback
callback
,
)
async
{
ByteData
response
;
try
{
final
MessageHandler
handler
=
_handlers
[
channel
];
if
(
handler
!=
null
)
{
response
=
await
handler
(
data
);
}
else
{
ui
.
channelBuffers
.
push
(
channel
,
data
,
callback
);
callback
=
null
;
}
}
catch
(
exception
,
stack
)
{
FlutterError
.
reportError
(
FlutterErrorDetails
(
exception:
exception
,
stack:
stack
,
library
:
'flutter web
shell
'
,
context:
ErrorDescription
(
'during a
plugin platform message call
'
),
library
:
'flutter web
plugins
'
,
context:
ErrorDescription
(
'during a
framework-to-plugin message
'
),
));
}
finally
{
if
(
callback
!=
null
)
{
...
...
@@ -111,7 +131,7 @@ class _PlatformBinaryMessenger extends BinaryMessenger {
FlutterError
.
reportError
(
FlutterErrorDetails
(
exception:
exception
,
stack:
stack
,
library
:
'flutter web
shell
'
,
library
:
'flutter web
plugins
'
,
context:
ErrorDescription
(
'during a plugin-to-framework message'
),
));
}
...
...
@@ -125,9 +145,6 @@ class _PlatformBinaryMessenger extends BinaryMessenger {
_handlers
.
remove
(
channel
);
else
_handlers
[
channel
]
=
handler
;
ui
.
channelBuffers
.
drain
(
channel
,
(
ByteData
data
,
ui
.
PlatformMessageResponseCallback
callback
)
async
{
await
handlePlatformMessage
(
channel
,
data
,
callback
);
});
}
@override
...
...
@@ -135,17 +152,24 @@ class _PlatformBinaryMessenger extends BinaryMessenger {
@override
void
setMockMessageHandler
(
String
channel
,
Future
<
ByteData
>
Function
(
ByteData
message
)
handler
)
{
String
channel
,
Future
<
ByteData
>
Function
(
ByteData
message
)
handler
,
)
{
throw
FlutterError
(
'Setting mock handlers is not supported on the platform side.'
);
'Setting mock handlers is not supported on the platform side.'
,
);
}
@override
bool
checkMockMessageHandler
(
String
channel
,
MessageHandler
handler
)
{
throw
FlutterError
(
'Setting mock handlers is not supported on the platform side.'
);
'Setting mock handlers is not supported on the platform side.'
,
);
}
}
/// The default [BinaryMessenger] for Flutter Web plugins.
/// The default [BinaryMessenger] for Flutter web plugins.
///
/// This is the value used for [webPluginRegistry]'s [PluginRegistry]
/// constructor argument.
final
BinaryMessenger
pluginBinaryMessenger
=
_PlatformBinaryMessenger
();
packages/flutter_web_plugins/test/plugin_event_channel_test.dart
View file @
8d923bf9
...
...
@@ -23,7 +23,7 @@ void main() {
webPluginRegistry
.
registerMessageHandler
();
});
test
(
'can send events to an
$EventChannel
'
,
()
async
{
test
(
'can send events to an
$EventChannel
(deprecated API)
'
,
()
async
{
const
EventChannel
listeningChannel
=
EventChannel
(
'test'
);
const
PluginEventChannel
<
String
>
sendingChannel
=
PluginEventChannel
<
String
>(
'test'
);
...
...
@@ -39,7 +39,23 @@ void main() {
await
controller
.
close
();
});
test
(
'can send errors to an
$EventChannel
'
,
()
async
{
test
(
'can send events to an
$EventChannel
'
,
()
async
{
const
EventChannel
listeningChannel
=
EventChannel
(
'test'
);
const
PluginEventChannel
<
String
>
sendingChannel
=
PluginEventChannel
<
String
>(
'test'
);
final
StreamController
<
String
>
controller
=
StreamController
<
String
>();
sendingChannel
.
setController
(
controller
);
expect
(
listeningChannel
.
receiveBroadcastStream
(),
emitsInOrder
(<
String
>[
'hello'
,
'world'
]));
controller
.
add
(
'hello'
);
controller
.
add
(
'world'
);
await
controller
.
close
();
});
test
(
'can send errors to an
$EventChannel
(deprecated API)'
,
()
async
{
const
EventChannel
listeningChannel
=
EventChannel
(
'test2'
);
const
PluginEventChannel
<
String
>
sendingChannel
=
PluginEventChannel
<
String
>(
'test2'
);
...
...
@@ -56,7 +72,24 @@ void main() {
await
controller
.
close
();
});
test
(
'receives a listen event'
,
()
async
{
test
(
'can send errors to an
$EventChannel
'
,
()
async
{
const
EventChannel
listeningChannel
=
EventChannel
(
'test2'
);
const
PluginEventChannel
<
String
>
sendingChannel
=
PluginEventChannel
<
String
>(
'test2'
);
final
StreamController
<
String
>
controller
=
StreamController
<
String
>();
sendingChannel
.
setController
(
controller
);
expect
(
listeningChannel
.
receiveBroadcastStream
(),
emitsError
(
predicate
<
dynamic
>((
dynamic
e
)
=>
e
is
PlatformException
&&
e
.
message
==
'Test error'
)));
controller
.
addError
(
'Test error'
);
await
controller
.
close
();
});
test
(
'receives a listen event (deprecated API)'
,
()
async
{
const
EventChannel
listeningChannel
=
EventChannel
(
'test3'
);
const
PluginEventChannel
<
String
>
sendingChannel
=
PluginEventChannel
<
String
>(
'test3'
);
...
...
@@ -72,7 +105,23 @@ void main() {
await
controller
.
close
();
});
test
(
'receives a cancel event'
,
()
async
{
test
(
'receives a listen event'
,
()
async
{
const
EventChannel
listeningChannel
=
EventChannel
(
'test3'
);
const
PluginEventChannel
<
String
>
sendingChannel
=
PluginEventChannel
<
String
>(
'test3'
);
final
StreamController
<
String
>
controller
=
StreamController
<
String
>(
onListen:
expectAsync0
<
void
>(()
{},
count:
1
));
sendingChannel
.
setController
(
controller
);
expect
(
listeningChannel
.
receiveBroadcastStream
(),
emitsInOrder
(<
String
>[
'hello'
]));
controller
.
add
(
'hello'
);
await
controller
.
close
();
});
test
(
'receives a cancel event (deprecated API)'
,
()
async
{
const
EventChannel
listeningChannel
=
EventChannel
(
'test4'
);
const
PluginEventChannel
<
String
>
sendingChannel
=
PluginEventChannel
<
String
>(
'test4'
);
...
...
@@ -92,5 +141,26 @@ void main() {
controller
.
add
(
'hello'
);
});
test
(
'receives a cancel event'
,
()
async
{
const
EventChannel
listeningChannel
=
EventChannel
(
'test4'
);
const
PluginEventChannel
<
String
>
sendingChannel
=
PluginEventChannel
<
String
>(
'test4'
);
final
StreamController
<
String
>
controller
=
StreamController
<
String
>(
onCancel:
expectAsync0
<
void
>(()
{}));
sendingChannel
.
setController
(
controller
);
final
Stream
<
dynamic
>
eventStream
=
listeningChannel
.
receiveBroadcastStream
();
StreamSubscription
<
dynamic
>
subscription
;
subscription
=
eventStream
.
listen
(
expectAsync1
<
void
,
dynamic
>((
dynamic
x
)
{
expect
(
x
,
equals
(
'hello'
));
subscription
.
cancel
();
}));
controller
.
add
(
'hello'
);
});
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment