From flutter-skills
Embeds native Android, iOS, or macOS views into a Flutter app. Use when integrating complex native components like maps or web views.
npx claudepluginhub gsmlg-dev/code-agent --plugin flutter-skillsThis skill uses the workspace's default tool permissions.
- [Platform Views Architecture](#platform-views-architecture)
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides MCP server integration in Claude Code plugins via .mcp.json or plugin.json configs for stdio, SSE, HTTP types, enabling external services as tools.
Platform Views allow embedding native views (Android, iOS, macOS) directly into a Flutter application, enabling the application of transforms, clips, and opacity from Dart.
Choose the appropriate implementation based on your performance and fidelity requirements:
SurfaceFlinger to compose both.
SurfaceView is problematic (breaks accessibility). Text magnifiers break unless Flutter is rendered into a TextureView.UIView is appended to the view hierarchy.
ShaderMask and ColorFiltered widgets are not supported. BackdropFilter has composition limitations.NSView).
Mitigate performance drops during complex Dart animations by rendering a screenshot of the native view as a placeholder texture while the animation runs.
Embed Flutter into existing web applications (Vanilla JS, React, Angular, etc.) using either Full Page mode or Embedded (Multi-view) mode.
iframe if you need to constrain the Flutter app without modifying the Flutter bootstrap process.divs). Requires multiViewEnabled: true during engine initialization.
app.addView() and app.removeView().runApp with runWidget.WidgetsBinding.instance.platformDispatcher.views and render them using ViewCollection and View widgets.Follow this sequential workflow to implement a Platform View on Android.
Task Progress:
If using Hybrid Composition, use PlatformViewLink, AndroidViewSurface, and PlatformViewsService.initSurfaceAndroidView.
If using Texture Layer, use the AndroidView widget.
Create a class implementing io.flutter.plugin.platform.PlatformView that returns your native android.view.View.
Create a factory extending PlatformViewFactory to instantiate your view.
Register the factory in your MainActivity.kt (or plugin) using flutterEngine.platformViewsController.registry.registerViewFactory.
Note: If your native view uses SurfaceView or SurfaceTexture, manually call invalidate on the View or its parent when content changes, as they do not invalidate themselves automatically.
Follow this sequential workflow to implement a Platform View on iOS.
Task Progress:
UiKitView.FlutterPlatformView) and Factory (FlutterPlatformViewFactory).AppDelegate.swift or the plugin registrar.Follow this sequential workflow to embed Flutter into an existing web DOM.
Task Progress:
flutter_bootstrap.js to enable multi-view.main.dart to use runWidget and ViewCollection.In flutter_bootstrap.js, initialize the engine with multiViewEnabled: true.
Use the returned app object to add views: app.addView({ hostElement: document.getElementById('my-div') }).
Replace runApp() with runWidget().
Create a root widget that listens to WidgetsBindingObserver.didChangeMetrics.
Map over WidgetsBinding.instance.platformDispatcher.views to create a View widget for each attached FlutterView, and wrap them all in a ViewCollection.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class NativeAndroidView extends StatelessWidget {
@override
Widget build(BuildContext context) {
const String viewType = 'my_native_view';
final Map<String, dynamic> creationParams = <String, dynamic>{};
return AndroidView(
viewType: viewType,
layoutDirection: TextDirection.ltr,
creationParams: creationParams,
creationParamsCodec: const StandardMessageCodec(),
);
}
}
_flutter.loader.load({
onEntrypointLoaded: async function(engineInitializer) {
let engine = await engineInitializer.initializeEngine({
multiViewEnabled: true,
});
let app = await engine.runApp();
// Add a view to a specific DOM element
let viewId = app.addView({
hostElement: document.querySelector('#flutter-host-container'),
initialData: { customData: 'Hello from JS' }
});
}
});
import 'dart:ui' show FlutterView;
import 'package:flutter/widgets.dart';
void main() {
runWidget(MultiViewApp(viewBuilder: (context) => const MyEmbeddedWidget()));
}
class MultiViewApp extends StatefulWidget {
final WidgetBuilder viewBuilder;
const MultiViewApp({super.key, required this.viewBuilder});
@override
State<MultiViewApp> createState() => _MultiViewAppState();
}
class _MultiViewAppState extends State<MultiViewApp> with WidgetsBindingObserver {
Map<Object, Widget> _views = {};
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_updateViews();
}
@override
void didChangeMetrics() => _updateViews();
void _updateViews() {
final newViews = <Object, Widget>{};
for (final FlutterView view in WidgetsBinding.instance.platformDispatcher.views) {
newViews[view.viewId] = _views[view.viewId] ?? View(
view: view,
child: Builder(builder: widget.viewBuilder),
);
}
setState(() => _views = newViews);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
Widget build(BuildContext context) {
return ViewCollection(views: _views.values.toList(growable: false));
}
}