How to visualize masks
Example code how to visualize SVG masks from Face Skin Analysis 3.0 (URLs should be received from API/Webhook separately).
Link to playground
Code
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:http/http.dart' as http;
import 'dart:typed_data';
void main() => runApp(const NetworkOverlayApp());
class NetworkOverlayApp extends StatefulWidget {
const NetworkOverlayApp({super.key});
@override
State<NetworkOverlayApp> createState() => _NetworkOverlayAppState();
}
class _NetworkOverlayAppState extends State<NetworkOverlayApp> {
// Replace with your signed URLs:
static const photoUrl = '';
static const svgUrl = '';
late Future<_Payload> _future;
@override
void initState() {
super.initState();
_future = _load();
}
Future<_Payload> _load() async {
// 1) Fetch image bytes & decode intrinsic size
final imgResp = await http.get(Uri.parse(photoUrl));
if (imgResp.statusCode != 200) throw Exception('Image load failed');
final imgBytes = Uint8List.fromList(imgResp.bodyBytes);
final imgCodec = await ui.instantiateImageCodec(imgBytes);
final frame = await imgCodec.getNextFrame();
final imgW = frame.image.width.toDouble();
final imgH = frame.image.height.toDouble();
// 2) Fetch SVG string (optional: strip <style> if flutter_svg chokes on it)
final svgRaw = await http.read(Uri.parse(svgUrl));
final svg = _sanitizeSvg(svgRaw); // remove <style>...</style> minimally
return _Payload(
imageBytes: imgBytes,
imageW: imgW,
imageH: imgH,
svgString: svg,
);
}
// Minimal sanitizer for CSS-in-SVG issues:
String _sanitizeSvg(String raw) {
final style = RegExp(r'<style[^>]*>[\s\S]*?<\/style>', multiLine: true);
return raw.replaceAll(style, '');
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.grey[200],
body: Center(
child: FutureBuilder<_Payload>(
future: _future,
builder: (context, snap) {
if (snap.connectionState != ConnectionState.done) {
return const SizedBox.square(
dimension: 48,
child: CircularProgressIndicator(),
);
}
if (snap.hasError || !snap.hasData) {
return const Icon(Icons.error, size: 32);
}
final p = snap.data!;
// 3) Paint both layers in the SAME intrinsic box (imgW × imgH)
final overlay = SizedBox(
width: p.imageW,
height: p.imageH,
child: Stack(
fit: StackFit.expand,
children: [
// Use Image.memory to avoid re-downloading
Image.memory(
p.imageBytes,
fit: BoxFit.fill, // map to the box exactly
),
SvgPicture.string(
p.svgString,
// Crucial: also fill the same box so viewBox maps 1:1
fit: BoxFit.fill,
// If your SVG paths already have fills, don't tint:
// colorFilter: const ColorFilter.mode(Colors.white70, BlendMode.srcIn),
),
],
),
);
// 4) Now scale that whole box together for display
return ClipRRect(
borderRadius: BorderRadius.circular(12),
child: FittedBox(
fit: BoxFit.contain, // or BoxFit.cover if you want cropping
child: overlay,
),
);
},
),
),
),
);
}
}
class _Payload {
final Uint8List imageBytes; // make this Uint8List, not List<int>
final double imageW;
final double imageH;
final String svgString;
_Payload({
required this.imageBytes,
required this.imageW,
required this.imageH,
required this.svgString,
});
}Last updated
Was this helpful?