3.47.1
Engine updated to Flutter 3.47.1 (5d53178869); flutter-embedded-linux
submodule bumped to latest upstream.
Highlights
NonisolatedNonsendingByDefaultenabled: unannotated async API inherits the
caller's actor, so platform channel round trips no longer hop out to the
cooperative pool and back.- Platform channel sends, and the codec work around them, now run on the
platform thread — as they do in Flutter's own channels, where a task queue is
the opt-out. - Plugin registration no longer races, and platform views work for the first
time.
Breaking changes
NonisolatedNonsendingByDefaultchanges the mangling of public API taking
async closures. Dependents need a rebuild.- The reply-less
FlutterMethodChannel.invoke(method:arguments:)and
FlutterBasicMessageChannel.send(message:)are now
@FlutterPlatformThreadActorand non-async; the underlying send is
synchronous, so theawaitonly ever bought the hop onto the platform actor.
Callers not already on the platform actor keep writingtry await; callers on
it drop theawait. FlutterPlugin.register(with:)andaddMethodCallDelegateare isolated to
the platform actor, as isFlutterPluginRegistrar.handleMethod.FlutterPlatformViewis now class-bound (: AnyObject) and covers the six
methods the embedder implements, addingclearFocus(),
resize(width:height:),touch(deviceId:eventType:x:y:)and
offset(top:left:). A value type can no longer conform.FlutterView.viewControlleris gone.FlutterViewis a handle onto the
embedder's view, vended afresh on each access; the platform views registry now
lives on the engine.
Channels and threading
withPriorityruns its block inline when no priority is given, so a caller
already on the platform actor stays there; previously it always spawned a
Task, which starts on the generic executor, so the engine send ran off the
platform thread despitesend()being@FlutterPlatformThreadActor— the
very hop the feature was meant to remove.- Both messengers route through an isolated
_send, so the priority path hops
back rather than calling the engine off the platform thread. - The desktop messenger's reply path drops an inner
Task.
Plugins
- Plugin registration spawned a task to reach the platform-actor-isolated
setMethodCallHandler, soregister()returned before the handler existed
and a message arriving in the gap surfaced in Dart as
MissingPluginException. Isolatingregister()andaddMethodCallDelegate
closes the gap.
Platform views
Platform views could never have worked before this release:
- The registration path was dead — nothing ever assigned
FlutterView.viewController— and would not have taken effect anyway, since
FlutterViewis a struct that bothengine.viewandregistrar.viewmint
afresh on every access, so the handler was stored into a copy. create()parsed its arguments viavalue as? [String: Any], butvalue(as:)
is a throwing method rather than a property, so the cast never succeeded.create()returnednilinstead of the texture id Dart composites from.- The touch case was spelled
"touchMethod"where Dart sends"touch";
offsetwas missing entirely;resize,clearFocus,touchandoffset
were unimplemented. - The view protocol admitted value types, so clearing focus on the previously
focused view mutated a copy.
Now: platform view state lives on the engine, and the registry is registered on
first use so it displaces the handler the embedder's view installs in its own
constructor. An app that registers no factory never creates it, and the embedder
keeps the channel exactly as before. The five methods the embedder stubs
(setDirection, acceptGesture, rejectGesture, enter, exit) throw
methodNotImplemented, as it does. Arguments are read off
AnyFlutterStandardCodable directly, since the generic bridge round-trips
through JSON, which is too costly for a per-touch path.
Examples
- Counter example dependencies refreshed;
analysis_options.yamladded.
Upgrade notes
- Rebuild dependents: enabling
NonisolatedNonsendingByDefaultchanges public
symbol mangling. - Drop
awaitfrom reply-lessinvoke(method:arguments:)/
send(message:)calls made from the platform actor; other callers are
unchanged. - Make
FlutterPlatformViewconformances classes and implement the four new
methods. - Remove any use of
FlutterView.viewController; platform view registration is
now automatic on first factory registration. - Replace uses of the removed
*Blockshims inCxxFlutterSwift.hwith the
embedder's ownFlutterDesktopMessengerSetCallback/
FlutterDesktopPluginRegistrarSetDestructionHandler.
3.47.0
Engine updated to Flutter 3.47.0 (5f77625673).
Highlights
- The C++ shim layer is gone. FlutterSwift now calls the eLinux embedder's
function-pointer C API directly through C++ interop, boxing its own closures
into the accompanyinguser_data, soCxxFlutterSwift.cppand its
Objective-C block typedefs have been deleted.
Breaking changes
-
The block-taking shims are removed from
CxxFlutterSwift.h:FlutterDesktopMessengerSendWithReplyBlockFlutterDesktopMessengerSetCallbackBlockFlutterDesktopPluginRegistrarSetDestructionHandlerBlockFlutterDesktopEngineSetView
Two of these only duplicated public API the embedder already vends
(FlutterDesktopMessengerSetCallback,
FlutterDesktopPluginRegistrarSetDestructionHandler); the fourth had no
callers, asFlutterEngine.setViewreachesflutter::FlutterELinuxEngine
through interop.
Memory and lifecycle fixes
- Every send-with-reply leaked its copied block: the engine only calls the
cleanup callback when the send outright fails. The Swift reply thunk now
consumes the retain. - The messenger callback setter stored a
_Block_copybut handed the engine the
uncopied block asuser_data, which only held together because nothing
freed the copy first. - Plugin registrar destructors are kept per registrar as a list rather than a
single entry. eLinux vends one registrar per engine however many plugins ask
for one, so the old map let the second plugin's handler evict the first,
leaving that plugin attached to a freed registrar afterStop().
Build
releaseadded to the excluded sources so SwiftPM does not pick up the build
directory.