Grid-style write-ups set whole products side by side. This one weighs something narrower: the two ways Flutter can host a native preview surface for a Face Filters SDK, judged through what actually sits above that surface — the look carousel, the per-element intensity controls, the animated foreground and border elements, and the lifecycle that keeps any of it on screen.
Those two ways are a texture-backed surface and a composited platform view. They differ in who composites each frame, which sounds abstract until you put a scrolling carousel over the preview and watch what happens during a transition.
One framing note first. Meishe effects reach Flutter as Flutter (supported through the short-video solution, for cross-framework teams), so what you host is a capture pipeline rather than a standalone widget library. The composition mechanics below belong to Flutter itself; which mode a given surface uses follows from the pipeline and platform, so verify that against your integration package rather than assuming it.
1. Product Positioning
Meishe Face Filters SDK is the filter and effects layer of the Face AR SDK family, evaluating real-time effects without pre-processing on the cross-platform audio/video processing engine used across the product family. One effect stack serves capture, timeline editing, and live streaming across iOS, Android, Web, Desktop (Windows), smart hardware, React Native, and Flutter through the short-video solution.
Content ships as data: individual elements and complete looks, with per-element intensity control, delivered as packages that change without a new binary.
There is no finished camera UI, so your Flutter code owns permissions, product flows, and upload in both arrangements.
2. Two composition models, read through the library layer
Flutter gives you two ways to put something native on screen, and the choice reaches your look UI more than teams expect.
Texture-backed surface. Native code renders into a surface Flutter owns as an external texture, and Flutter composites it inside its own raster step. Because it is drawn in Flutter’s pass, anything you overlay — a carousel, badge, or timer — is composited by the same pipeline that drew it.
Composited platform view. The native view is inserted into Flutter’s view hierarchy, and Flutter tells the host compositor where it should appear. The pixels are produced by the platform, and anything your widgets draw near it arrives from a different pipeline.
| Library concern | Texture-backed surface | Composited platform view |
|---|---|---|
| Look carousel overlaying the preview | Composite by Flutter, in-pipeline | Flutter chrome drawn alongside platform pixels |
| Per-element intensity controls during recording | Stable while widgets move | More sensitive to concurrent resizes |
| Animated foreground and border elements | Travel with the preview | Platform-owned pixels beneath Flutter chrome |
| Browsing looks while the camera warms | Ordinarily fine | Occasionally shows a transitional gap |
| Who you debug a black frame with | Texture registration and lifecycle | View hierarchy and composition |
Read the final column as questions to answer on your own devices. Neither model is universally stronger, and plenty of teams end up using both because different surfaces in one app get navigated very differently.

3. Arrangement one: texture-backed surface
The attraction is that the preview behaves like an ordinary widget, so a carousel of twenty looks can slide, scale, and clip over it without asking anything of the native layer.
The snippet below is schematic. Class names and signatures are illustrative and are not the shipped API surface.
// Illustrative texture-backed preview with a look carousel above it.
class EffectPreviewStack extends StatelessWidget {
final MeisheShortVideoController controller;
final List<String> lookIds;
const EffectPreviewStack({super.key, required this.controller, required this.lookIds});
@override
Widget build(BuildContext context) {
return ValueListenableBuilder<int?>(
valueListenable: controller.textureId,
builder: (context, textureId, _) {
if (textureId == null) return const Placeholder();
return Stack(
children: [
Positioned.fill(child: Texture(textureId: textureId)),
Align(
alignment: Alignment.bottomCenter,
child: LookCarousel(
lookIds: lookIds,
onSelected: (id) async {
await controller.assets.resolve(id);
await controller.looks.apply(id);
},
),
),
],
);
},
);
}
}
What you are testing is lifecycle rather than pixels: that the texture registers once, survives navigation into a detail route and back, and is disposed without leaving a stale handle. Failures present as a frozen last frame rather than an exception, which is why the background-and-resume check matters more than visual inspection.
4. Arrangement two: composited platform view
The attraction is fidelity to the host platform: the preview is a genuine native view, so anything the platform does with camera surfaces — focus behaviour, safe-area handling, rotation — is preserved rather than approximated.
The snippet below is schematic. Class names and signatures are illustrative and are not the shipped API surface.
// Illustrative platform-view-style host with room-sized layout.
Widget buildPreview(MeisheShortVideoController controller) {
return SizedBox.expand(
child: MeishePlatformPreview(
controller: controller,
onSurfaceReady: () => controller.resume(),
onSurfaceLost: () => controller.pause(),
),
);
}
The discipline this arrangement demands is layout conservatism. Give the surface a stable, explicit size; avoid resizing it mid-recording; keep the carousel outside its box rather than overlapping it unless testing says otherwise. Most reported artefacts trace back to a preview being resized while compositing, not to the composition model itself.
5. Running the two fairly
Fix the protocol before you start; anything decided after seeing results is rationalisation.
Shared corpus. Ten looks drawing on twenty shared elements, including two you plan to retire, plus two using animated foreground and border elements. Apply every one through both arrangements while recording.
Browsing under load. Scroll the carousel during a recording, switch looks while an animated element plays, then navigate away and back. This is where the models diverge, and the difference is visual, so capture it.
Lifecycle stress. Background and resume twenty times per arrangement, then again mid-recording. Note which failures recover silently.
Content refresh. Ship a changed package through your channel with no rebuild and confirm both arrangements pick it up, including looks that only reference the changed element.
Counted ownership. Note how much Dart and native code each arrangement required and where a fix had to land. That predicts maintenance more reliably than any behaviour observed on day one.
Keep the clips. Frozen frames and transition gaps are visible on review even when they pass unnoticed while holding the device.
6. What neither arrangement changes
Colour fidelity is identical in both. Rendered pigment lands inside the exposure and white balance of each frame, so the same shade reads differently in daylight, indoors, and under mixed sources, and tone, undertone, and texture vary person to person. Whichever player draws the frame, the output is a preview, not a colour measurement.
Compression sets a second ceiling. Many captures arrive from low-resolution, heavily compressed front cameras, and fine detail degrades first around the eyes and along the lip line. No composition mode restores what the encoder discarded.
Content structure is the third invariant, and the useful one. Because a look references elements rather than containing them, one element edit propagates everywhere the look is used, and retiring an element stays a review rather than a rebuild — regardless of which model composited the preview. Composition decides how pixels reach the screen; the library model decides how expensive your second seasonal drop is, and only one of those is a long-term cost.
FAQ
Which arrangement should we default to?
Start with whichever your integration already provides, and only move if testing on your own device mix shows a problem. A difference that never reaches users does not justify a rewrite, since both sit above the same effect pipeline.
Does the choice change how we author looks?
No. Assets are content rather than code, so the element and look structure is identical either way, including per-element intensity levels. Only composition differs.
Can one app use both?
Yes. Teams frequently use one arrangement for the main capture screen and the other for a secondary surface with different navigation characteristics. Keep the eventual surface behind one widget interface so the decision stays reversible.
Why does the carousel matter so much here?
Because that is where Flutter chrome meets native pixels, and it is the surface users touch most. Most visible artefacts appear while scrolling or transitioning rather than during steady recording.
Where did the camera interface go?
There is not one. The SDK ships no finished camera UI, so your Flutter code owns permissions, controls, gallery access, and upload, while the hosted pipeline owns capture and preview.
Ready to see it in your own stack? Request a free trial or talk to our integration team — we’ll help you scope the modules you actually need.
