<!-- skill: Flutter Advanced | version: 1.0.0 | description: CustomPainter, isolates, platform channels and FFI for Flutter | category: Mobile -->
## Instructions
- CustomPainter done right: extend `CustomPainter`, override `shouldRepaint(old) => old.data != data`; wrap in `RepaintBoundary`, `size: Size.infinite` handled via `LayoutBuilder`; cache `Path`/`Paint` objects, never allocate in `paint()`.
- Canvas perf: batch draws (`drawPoints`, `drawPath`), `canvas.clipRect` before heavy scenes, `isComplex: true, willChange: true` on animating RepaintBoundary; profile with `flutter build --profile` + ShaderWarmUp for jank.
- Isolates for CPU work: `await compute(parseJson, rawBody)` or `Isolate.run(() => heavyFilter(bytes))`; keep isolate fns top-level/static, args primitive/serializable; stream progress via `ReceivePort` for >2s jobs.
- Platform channels: `const ch = MethodChannel('com.app/sensors')`; Dart `await ch.invokeMethod<Map>('stream')`; Kotlin `MethodChannel(flutterEngine.dartExecutor, name).setMethodCallHandler`; Swift `FlutterMethodChannel(name:binaryMessenger:)`; error via `result.error("E1","msg",null)`.
- Prefer Pigeon for type-safe channels: define `sensor_api.dart` (`@HostApi getBattery(): double`), run `dart run pigeon --input pigeon/sensor.dart`; commit generated Kotlin/Swift/Dart, never hand-edit.
- FFI for hot loops: `dart:ffi` + `flutter_rust_bridge` or C via `NativeLibrary.lookup`; ship `.so`/`.xcframework` per ABI, load with `DynamicLibrary.open`; keep FFI off UI thread (wrap in isolate).
- Texture interop: `TextureRegistry` / `ExternalTexture` for camera/ML frames; dispose `textureId` on widget dispose; throttle to 30fps to save battery.
- Flavor parity: `android { flavorDimensions "env"; productFlavors { dev { applicationIdSuffix ".dev" }; prod {} } }` + Xcode schemes `dev/prod` with matching bundle IDs; `--flavor dev --dart-define=API_URL=...` everywhere including CI.
- Native parity checklist: permissions mirrored (manifest + Info.plist), icons/splash per flavor, ProGuard/R8 keep rules for channels/FFI (`-keep class com.app.**`), Bitcode/dSYM upload for iOS.
- Memory: `ImageCache.maximumSizeBytes = 100 << 20`, `dispose()` controllers/animations, `WeakReference` in long-lived listeners; leak-check with DevTools Memory + `leak_tracker`.
- Blend with existing multiplatform skill: this file covers render/isolate/channel/FFI depth; defer basic Riverpod/go_router/flavors setup to `flutter-multiplatform`.
- Test native bridge: `TestDefaultBinaryMessengerBinding` mock handler for MethodChannel unit tests; `flutter test --platform chrome` for painter goldens; on-device `integration_test` for FFI/channel round-trip.
## Examples
- Waveform painter: cached Path + RepaintBoundary, 60fps scrub, compute() decodes peaks off-thread.
- Image filter: Rust FFI blur via isolate, texture preview, flavor-aware API upload.
- Sensor bridge: Pigeon `getStepCount()` with Kotlin Health Services + Swift CoreMotion parity.
## Workflows
1. Profile first (DevTools timeline) → decide Painter vs FFI vs isolate.
2. Scaffold channel (Pigeon) or FFI binding + flavor defines.
3. Implement painter/isolate with caches + dispose + error states.
4. Parity pass: manifest/plist perms, R8 keeps, icons, bundle IDs per flavor.
5. Test: mock channel unit + golden painter + on-device integration.
6. Release: profile build, shader warmup, mapping/dSYM upload.
## Templates
- Painter + isolate:
```dart
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
List<double> decodePeaks(List<int> bytes) {
  // top-level for compute(): heavy DSP here
  return bytes.map((b) => b / 255.0).toList();
}
class WavePainter extends CustomPainter {
  final List<double> peaks;
  final Paint _paint = Paint()..color = Colors.blue..strokeWidth = 2;
  WavePainter(this.peaks);
  @override
  void paint(Canvas canvas, Size size) {
    final path = Path();
    for (var i = 0; i < peaks.length; i++) {
      final x = i / peaks.length * size.width, y = size.height * (1 - peaks[i]);
      i == 0 ? path.moveTo(x, y) : path.lineTo(x, y);
    }
    canvas.drawPath(path, _paint);
  }
  @override
  bool shouldRepaint(covariant WavePainter old) => old.peaks != peaks;
}
// usage: peaks = await compute(decodePeaks, rawBytes);
// RepaintBoundary(child: CustomPaint(painter: WavePainter(peaks), size: Size.infinite))
```
- Pigeon + FFI:
```dart
// pigeon/sensor.dart
import 'package:pigeon/pigeon.dart';
@HostApi() abstract class SensorApi { double getBattery(); }
// dart run pigeon --input pigeon/sensor.dart --dart_out lib/sensor.g.dart
// --kotlin_out android/app/src/main/kotlin/SensorApi.kt --swift_out ios/SensorApi.swift
// ffi
import 'dart:ffi' as ffi;
final lib = ffi.DynamicLibrary.open('libfilter.so');
typedef BlurC = ffi.Void Function(ffi.Pointer<ffi.Uint8>, ffi.Int32);
Future<void> blurInIsolate(List<int> bytes) =>
  Isolate.run(() { /* call lib.lookup<ffi.NativeFunction<BlurC>>('blur') */ });
```
## Tools
- SARAH shell_run for `dart run pigeon`, `flutter build --profile`, DevTools timeline/memory.
- SARAH file_write for painter/isolate/pigeon/FFI scaffolds; web_fetch for CustomPainter + Pigeon + FFI docs.
## Advanced
- Impeller: test on `--enable-impeller` + Vulkan/Metal devices; precompile shaders (`SkSL warmup` → `flutter build bundle --write-sksl`), watch `RasterCache` misses.
- Rust bridge: `flutter_rust_bridge_codegen generate` for zero-copy structs; pin NDK r26 + Xcode 15; strip symbols for size (`split-debug-info`).
- Background: `flutter_background_service` + native FGS for audio/tracking; never run isolates forever — checkpoint + WorkManager reschedule.
## Documentation
- https://api.flutter.dev/flutter/rendering/CustomPainter-class.html
- https://docs.flutter.dev/platform-integration/platform-channels
- https://pub.dev/packages/pigeon
- https://dart.dev/guides/libraries/c-interop
