flutter-inspector-image
Image debugging specialist. Use for cache analysis and memory usage inspection
| 항목 | 내용 |
|---|---|
| 모델 | haiku |
| 사용 도구 | Read, Glob, Grep |
| 연계 스킬 | flutter-inspector |
A specialized agent for analyzing image cache and memory usage at runtime.
Triggers#
Automatically activated when @flutter-inspector-image is invoked or the following keywords are detected:
- Image cache, memory
- Image loading, optimization
- Cache size, clear
MCP Tools#
img_get_cache_stats#
Returns image cache statistics.
{
"name": "img_get_cache_stats",
"description": "Image cache statistics",
"inputSchema": {
"type": "object",
"properties": {}
}
}
Response example:
{
"cache": {
"currentSize": 52428800,
"currentSizeFormatted": "50 MB",
"maximumSize": 104857600,
"maximumSizeFormatted": "100 MB",
"usagePercent": 50,
"liveImageCount": 25,
"pendingImageCount": 3
},
"memory": {
"currentUsage": 157286400,
"currentUsageFormatted": "150 MB",
"peakUsage": 209715200,
"peakUsageFormatted": "200 MB"
},
"network": {
"cachedImages": 120,
"totalDownloaded": 314572800,
"totalDownloadedFormatted": "300 MB"
}
}
img_analyze_warnings#
Analyzes image-related warnings and issues.
{
"name": "img_analyze_warnings",
"description": "Image warning analysis",
"inputSchema": {
"type": "object",
"properties": {
"threshold": {
"type": "integer",
"description": "Warning threshold (MB)",
"default": 5
}
}
}
}
Response example:
{
"warnings": [
{
"type": "oversized",
"severity": "high",
"message": "Image is 4x larger than display size",
"image": "banner_home.png",
"details": {
"displaySize": "390x200",
"actualSize": "1560x800",
"wastedMemory": "4.7 MB"
},
"suggestion": "Recommend using cacheWidth/cacheHeight"
},
{
"type": "memory",
"severity": "medium",
"message": "Cache usage exceeds 75%",
"details": {
"usage": "75 MB / 100 MB"
},
"suggestion": "Recommend clearing old cache"
},
{
"type": "duplicate",
"severity": "low",
"message": "Same image cached at different sizes",
"image": "profile_user123.jpg",
"details": {
"variants": ["100x100", "200x200", "400x400"]
},
"suggestion": "Recommend using consistent sizes"
}
],
"score": 65,
"grade": "C"
}
img_clear_cache#
Clears the image cache.
{
"name": "img_clear_cache",
"description": "Clear image cache",
"inputSchema": {
"type": "object",
"properties": {
"type": {
"type": "string",
"description": "Clear type",
"enum": ["all", "memory", "disk", "expired"],
"default": "expired"
}
}
}
}
Response example:
{
"cleared": {
"type": "expired",
"freedMemory": 31457280,
"freedMemoryFormatted": "30 MB",
"removedImages": 45
},
"after": {
"currentSize": 20971520,
"currentSizeFormatted": "20 MB",
"liveImageCount": 15
}
}
App Integration Code#
// lib/debug/mcp_image_tools.dart
import 'package:mcp_toolkit/mcp_toolkit.dart';
import 'package:flutter/widgets.dart';
import 'package:cached_network_image/cached_network_image.dart';
class ImageAnalyzer {
static final instance = ImageAnalyzer._();
ImageAnalyzer._();
final List<Map<String, dynamic>> _loadedImages = [];
void trackImage({
required String url,
required Size displaySize,
required Size actualSize,
required int bytes,
}) {
_loadedImages.add({
'url': url,
'displaySize': displaySize,
'actualSize': actualSize,
'bytes': bytes,
'loadedAt': DateTime.now(),
});
}
Map<String, dynamic> getCacheStats() {
final imageCache = PaintingBinding.instance.imageCache;
return {
'cache': {
'currentSize': imageCache.currentSize,
'currentSizeFormatted': _formatBytes(imageCache.currentSize),
'maximumSize': imageCache.maximumSize,
'maximumSizeFormatted': _formatBytes(imageCache.maximumSize),
'usagePercent': (imageCache.currentSize / imageCache.maximumSize * 100).round(),
'liveImageCount': imageCache.liveImageCount,
'pendingImageCount': imageCache.pendingImageCount,
},
};
}
List<Map<String, dynamic>> analyzeWarnings({int threshold = 5}) {
final warnings = <Map<String, dynamic>>[];
final thresholdBytes = threshold * 1024 * 1024;
for (final img in _loadedImages) {
final displaySize = img['displaySize'] as Size;
final actualSize = img['actualSize'] as Size;
// Oversize check
if (actualSize.width > displaySize.width * 2 ||
actualSize.height > displaySize.height * 2) {
warnings.add({
'type': 'oversized',
'severity': 'high',
'message': 'Image is larger than display size',
'image': img['url'],
'details': {
'displaySize': '${displaySize.width.toInt()}x${displaySize.height.toInt()}',
'actualSize': '${actualSize.width.toInt()}x${actualSize.height.toInt()}',
},
'suggestion': 'Recommend using cacheWidth/cacheHeight',
});
}
}
// Cache usage check
final cache = PaintingBinding.instance.imageCache;
if (cache.currentSize > cache.maximumSize * 0.75) {
warnings.add({
'type': 'memory',
'severity': 'medium',
'message': 'Cache usage exceeds 75%',
'suggestion': 'Recommend clearing old cache',
});
}
return warnings;
}
Map<String, dynamic> clearCache(String type) {
final cache = PaintingBinding.instance.imageCache;
final beforeSize = cache.currentSize;
switch (type) {
case 'all':
case 'memory':
cache.clear();
break;
case 'expired':
cache.clearLiveImages();
break;
}
return {
'cleared': {
'type': type,
'freedMemory': beforeSize - cache.currentSize,
'freedMemoryFormatted': _formatBytes(beforeSize - cache.currentSize),
},
'after': {
'currentSize': cache.currentSize,
'currentSizeFormatted': _formatBytes(cache.currentSize),
},
};
}
String _formatBytes(int bytes) {
if (bytes < 1024) return '$bytes B';
if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB';
if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)} GB';
}
}
void registerImageTools() {
if (!kDebugMode) return;
addMcpTool(MCPCallEntry.tool(
handler: (_) => MCPCallResult(
message: 'Cache stats',
parameters: ImageAnalyzer.instance.getCacheStats(),
),
definition: MCPToolDefinition(
name: 'img_get_cache_stats',
description: 'Image cache statistics',
inputSchema: {'type': 'object', 'properties': {}},
),
));
addMcpTool(MCPCallEntry.tool(
handler: (params) {
final threshold = params['threshold'] as int? ?? 5;
return MCPCallResult(
message: 'Image warnings',
parameters: {
'warnings': ImageAnalyzer.instance.analyzeWarnings(threshold: threshold),
},
);
},
definition: MCPToolDefinition(
name: 'img_analyze_warnings',
description: 'Image warning analysis',
inputSchema: {
'type': 'object',
'properties': {
'threshold': {'type': 'integer', 'default': 5},
},
},
),
));
addMcpTool(MCPCallEntry.tool(
handler: (params) {
final type = params['type'] as String? ?? 'expired';
return MCPCallResult(
message: 'Cache cleared',
parameters: ImageAnalyzer.instance.clearCache(type),
);
},
definition: MCPToolDefinition(
name: 'img_clear_cache',
description: 'Clear image cache',
inputSchema: {
'type': 'object',
'properties': {
'type': {
'type': 'string',
'enum': ['all', 'memory', 'disk', 'expired'],
'default': 'expired',
},
},
},
),
));
}
Usage Examples#
Check cache status#
Q: Show me the image cache status
A: Run img_get_cache_stats
-> 50 MB / 100 MB (50%), 25 images
Analyze image issues#
Q: Are there any image optimization issues?
A: Run img_analyze_warnings
-> 3 warnings: 2 oversized, 1 memory, grade C
Clear cache#
Q: Clear the image cache
A: Run img_clear_cache type="expired"
-> 30 MB freed, 45 images removed
Common Problem Diagnosis#
Memory warning#
1. Check current usage with img_get_cache_stats
2. Identify problematic images with img_analyze_warnings
3. Clear cache with img_clear_cache
4. Consider applying cacheWidth/cacheHeight
Slow image loading#
1. Check cache hit rate with img_get_cache_stats
2. Check network image downloads (@flutter-inspector-network)
3. Review image size optimization
App crash (OOM)#
1. Check peak memory with img_get_cache_stats
2. Find oversized images with img_analyze_warnings
3. Apply large image optimization
4. Consider adjusting maximum cache size
Optimization Recommendations#
Image Loading Patterns#
// CORRECT: Specify cache size
Image.network(
imageUrl,
cacheWidth: 200,
cacheHeight: 200,
)
// CORRECT: Use CachedNetworkImage
CachedNetworkImage(
imageUrl: imageUrl,
memCacheWidth: 200,
placeholder: (context, url) => CircularProgressIndicator(),
)
// WRONG: Loading at original size
Image.network(imageUrl) // 4000x4000 image displayed at 100x100
Cache Settings#
// main.dart
void main() {
// Cache size configuration
PaintingBinding.instance.imageCache.maximumSize = 100 * 1024 * 1024; // 100 MB
PaintingBinding.instance.imageCache.maximumSizeBytes = 200 * 1024 * 1024; // 200 MB
runApp(const MyApp());
}
Related Agents#
@flutter-inspector: Master inspector@flutter-image-optimizer: Image optimization specialist agent@flutter-inspector-log: Image loading related logs