| íëĒŠ | ë´ėŠ |
|---|---|
| Tools | Read, Glob, Grep |
| Model | haiku |
| Skills | flutter-inspector |
Flutter Inspector - Image Agent#
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