Rendering Native Content in Flutter: Platform Views vs Texture Widget
Originally published on Medium.
A practical guide to the two primary ways to display native Android/iOS content inside Flutter, illustrated with a camera SDK example.
Why Flutter needs native rendering
Flutter renders almost everything itself using Skia (or Impeller on newer versions). Widgets, animations, buttons — all painted by Flutter’s own engine onto a single canvas.
But some SDKs expose mature native UI components that Flutter simply cannot repaint:
- Camera
- Maps
- WebView
- Video player
- AR SDKs
- Payment SDKs
Flutter needs a bridge: a way to host or consume native output without giving up control of the rest of the screen.
All rendering approaches
Flutter provides several mechanisms for native rendering. Two are primary (covered in depth in this guide); the rest are specialised.
| Approach | What it does | Typical use |
|---|---|---|
| Platform Views | Embeds a real native View (Android) or UIView (iOS) inside Flutter’s widget tree |
Maps, WebView, payment SDKs |
| Texture widget | Displays GPU-produced pixel frames from native code. No View hierarchy | Camera, video, OpenGL/Vulkan |
| Hybrid Composition | Modern Platform View variant. Flutter places a real Android View in the hierarchy with correct z-order and accessibility |
Modern Android native views |
| Virtual Display | Older Platform View variant. Native view renders into a virtual off-screen display. Largely superseded by Hybrid Composition | Older Android versions |
| Platform Channels only | No native rendering at all. Flutter draws the UI, native handles logic | Bluetooth, NFC, biometrics, sensors |
| Bitmap / screenshot | Native renders to a bitmap; Flutter displays it via Image.memory(). Unsuitable for live or interactive content |
Charts, PDF previews |
This guide focuses on Platform Views and Texture widget — the two primary techniques for live native rendering in Flutter.
Approach 1: Platform Views
What is a Platform View?
A Platform View embeds an actual native view inside Flutter. On Android this means View, SurfaceView, PreviewView, WebView, MapView, and so on. On iOS it wraps a UIView.
Flutter reserves an area on screen while the OS renders the native view. Think of it as a mini native app embedded inside your widget tree.
Architecture
Flutter never draws the camera preview. Android does.
Flutter widget tree
│
▼
AndroidView / UiKitView
│
▼
Native View (PreviewView, MapView, WebView…)
│
▼
OS layout, drawing, measuring, touch
Rendering pipeline
Android owns layout, drawing, measuring, and touch events. Flutter only owns positioning.
SDK / CameraX → PreviewView (native)
│
▼
Platform View hole in Flutter
│
▼
User sees native pixels in place
Touch / event flow
Because the native view is real, the OS delivers gestures to it first. Flutter is not the owner of those events inside the hole.
Camera example (Platform View)
AndroidView(
viewType: 'camera_preview',
)
┌────────────────────────────┐
│ Text / Buttons │
│ │
│ AndroidView │
│ ┌────────────────────┐ │
│ │ PreviewView │ │
│ │ (native Android) │ │
│ └────────────────────┘ │
│ │
│ Capture Button │
└────────────────────────────┘
The preview you see is PreviewView, not Flutter.
Advantages
- Supports any native UI component
- Native accessibility out of the box
- Native gesture handling
- Existing SDK works without modification
- Easy integration path
Drawbacks
- Higher rendering cost
- More expensive during scrolling
- More memory usage
- Complex view composition
Approach 2: Texture widget
What is a Texture?
A Texture is not a View. That is the biggest misconception.
The Texture widget simply displays pixels produced by native code. Flutter does not know (or care) whether those pixels came from a camera, a video decoder, OpenGL, Vulkan, or a game engine.
No Android View hierarchy is involved at all. Frames flow from native code through a SurfaceTexture into Flutter’s TextureRegistry, which assigns a numeric ID. Flutter’s Texture widget renders that GPU texture directly.
Architecture
Notice what is missing: there is no Android View.
Camera / CameraX / decoder / OpenGL
│
▼
SurfaceTexture (native draws frames)
│
▼
TextureRegistry (Flutter assigns textureId)
│
▼
Texture(textureId: id) ← Flutter paints GPU pixels
Rendering pipeline
Flutter only paints a GPU texture. Nothing else. That is why this path is cheaper than composing a real native view.
Touch / gesture flow
Because there is no native View, Flutter must handle all gestures. They are forwarded via MethodChannel to native code:
User touch
│
▼
Flutter GestureDetector
│
▼
MethodChannel
│
▼
Native camera (zoom, flash, capture)
Camera example (Texture)
Texture(
textureId: textureId,
)
┌───────────────────────────────┐
│ │
│ Flutter Widgets │
│ │
│ ┌─────────────────────────┐ │
│ │ │ │
│ │ Texture Widget │ │ ← GPU pixels, no View
│ │ │ │
│ └─────────────────────────┘ │
│ │
│ Flutter Capture Button │
└───────────────────────────────┘
Advantages
- Extremely smooth rendering
- Lower latency
- Better scrolling and animations
- Great for video and camera preview
- No view hierarchy overhead
Drawbacks
- Not interactive: pixels only
- No native controls or text fields
- Flutter must implement all gestures
- Extra communication via platform channels
Side-by-side comparison
| Platform Views | Texture widget | |
|---|---|---|
| What you embed | A real Android View / iOS UIView |
GPU frames (pixels only) |
| Who draws | The OS | Flutter paints a texture |
| View hierarchy | Yes | No |
| Touch | Native view handles it | Flutter, then MethodChannel |
| Accessibility | Native, out of the box | You build it in Flutter |
| Best for | Maps, WebView, payment UI | Camera, video, OpenGL/Vulkan |
| Cost | Higher (composition, memory, scroll) | Lower latency, smoother |
Camera SDK: which approach does the industry use?
Most camera Flutter plugins (including the official camera package) use the Texture approach for the live preview. It delivers smoother 60fps rendering and lower latency.
Flutter widgets are overlaid on top for the capture button, zoom slider, and flash controls.
Combined architecture used in real plugins:
Flutter
├── GestureDetector (pinch-to-zoom, etc.)
├── Texture Widget (live camera preview via GPU)
└── Flutter UI widgets (capture button, timer, etc.)
Android
├── Camera / CameraX
├── SurfaceTexture (frame production)
└── TextureRegistry (bridges frames to Flutter)
Communication
└── MethodChannel / EventChannel (zoom, flash, capture, events)
SurfaceTexture is the canvas where native code draws frames. TextureRegistry is Flutter’s manager that registers and exposes those canvases to Flutter.
When to use which
Choose Platform View when:
- You need a real Android/iOS View
- The SDK already exposes a View
- Native gestures are required
- Accessibility support is needed
- You are embedding WebView, Maps, or a payment SDK
Choose Texture widget when:
- Rendering camera frames
- Playing video
- Rendering OpenGL or Vulkan content
- Performance and latency are critical
- No native UI hierarchy is needed
Key takeaways
- Platform Views embed an actual native View inside Flutter. Android/iOS owns layout, drawing, measuring, and touch events. Flutter only owns positioning.
- Texture displays only GPU-rendered pixels. No View hierarchy. No layout overhead. Just frames.
- Platform Views are ideal for interactive native UI (WebView, Maps, payment sheets).
- Texture is ideal for high-performance rendering (camera preview, video playback, AR/OpenGL).
- In real-world plugins, these two approaches are often combined: Texture for rendering, platform channels for control, Flutter widgets for the UI layer on top.