| 항목 | 내용 |
|---|---|
| Tools | Read, Glob, Grep |
| Model | inherit |
| Skills | flutter-inspector |
Flutter Inspector - UI Agent#
위젯 트리를 런타임에서 검사하고 분석하는 전문 에이전트입니다.
트리거#
@flutter-inspector-ui 또는 다음 키워드 감지 시 자동 활성화:
- 위젯 트리, 레이아웃
- 오버플로우, 렌더링
- UI 검사, 화면 분석
MCP 도구#
ui_get_widget_tree#
현재 화면의 위젯 트리를 반환합니다.
{
"name": "ui_get_widget_tree",
"description": "위젯 트리 조회",
"inputSchema": {
"type": "object",
"properties": {
"depth": {
"type": "integer",
"description": "트리 깊이",
"default": 5
},
"includeRenderObjects": {
"type": "boolean",
"description": "RenderObject 정보 포함",
"default": false
}
}
}
}
응답 예시:
{
"tree": {
"type": "MaterialApp",
"children": [
{
"type": "Scaffold",
"children": [
{
"type": "AppBar",
"properties": {"title": "Home"}
},
{
"type": "ListView",
"children": [
{"type": "ListTile", "properties": {"title": "Item 1"}},
{"type": "ListTile", "properties": {"title": "Item 2"}}
]
}
]
}
]
},
"totalWidgets": 15
}
ui_find_widgets#
특정 타입의 위젯을 검색합니다.
{
"name": "ui_find_widgets",
"description": "위젯 검색",
"inputSchema": {
"type": "object",
"properties": {
"type": {
"type": "string",
"description": "위젯 타입명 (예: Text, Container, ListView)"
},
"key": {
"type": "string",
"description": "위젯 Key 값"
}
}
}
}
응답 예시:
{
"widgets": [
{
"type": "Text",
"path": "MaterialApp/Scaffold/Column/Text",
"properties": {
"data": "Hello World",
"style": {"fontSize": 16, "fontWeight": "bold"}
}
},
{
"type": "Text",
"path": "MaterialApp/Scaffold/Column/Row/Text",
"properties": {
"data": "Subtitle",
"style": {"fontSize": 14}
}
}
],
"count": 2
}
ui_get_screen_info#
현재 화면 정보를 반환합니다.
{
"name": "ui_get_screen_info",
"description": "화면 정보 조회",
"inputSchema": {
"type": "object",
"properties": {}
}
}
응답 예시:
{
"screen": {
"width": 390,
"height": 844,
"pixelRatio": 3.0,
"orientation": "portrait",
"padding": {
"top": 47,
"bottom": 34,
"left": 0,
"right": 0
}
},
"device": {
"platform": "iOS",
"isPhysicalDevice": true
}
}
ui_find_overflow#
오버플로우 문제가 있는 위젯을 찾습니다.
{
"name": "ui_find_overflow",
"description": "오버플로우 위젯 검색",
"inputSchema": {
"type": "object",
"properties": {}
}
}
응답 예시:
{
"overflows": [
{
"type": "Row",
"path": "Scaffold/Column/Row",
"issue": "RIGHT OVERFLOW by 24.0 pixels",
"size": {"width": 414, "height": 50},
"constraints": {"maxWidth": 390}
}
],
"count": 1
}
ui_get_text_widgets#
모든 텍스트 위젯과 내용을 반환합니다.
{
"name": "ui_get_text_widgets",
"description": "텍스트 위젯 조회",
"inputSchema": {
"type": "object",
"properties": {
"includeStyle": {
"type": "boolean",
"description": "스타일 정보 포함",
"default": false
}
}
}
}
응답 예시:
{
"textWidgets": [
{
"path": "Scaffold/AppBar/Text",
"text": "홈",
"style": {"fontSize": 20, "fontWeight": "w600"}
},
{
"path": "Scaffold/Body/Column/Text",
"text": "환영합니다",
"style": {"fontSize": 16}
}
],
"count": 2
}
앱 통합 코드#
// lib/debug/mcp_ui_tools.dart
import 'package:mcp_toolkit/mcp_toolkit.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart';
void registerUITools() {
if (!kDebugMode) return;
addMcpTool(MCPCallEntry.tool(
handler: (_) {
final binding = WidgetsBinding.instance;
final renderView = binding.renderView;
return MCPCallResult(
message: 'Screen info',
parameters: {
'screen': {
'width': renderView.size.width,
'height': renderView.size.height,
'pixelRatio': binding.window.devicePixelRatio,
},
},
);
},
definition: MCPToolDefinition(
name: 'ui_get_screen_info',
description: '화면 정보 조회',
inputSchema: {'type': 'object', 'properties': {}},
),
));
addMcpTool(MCPCallEntry.tool(
handler: (params) {
final depth = params['depth'] as int? ?? 5;
final tree = _buildWidgetTree(depth);
return MCPCallResult(
message: 'Widget tree',
parameters: {'tree': tree},
);
},
definition: MCPToolDefinition(
name: 'ui_get_widget_tree',
description: '위젯 트리 조회',
inputSchema: {
'type': 'object',
'properties': {
'depth': {'type': 'integer', 'default': 5},
},
},
),
));
// ui_find_widgets, ui_find_overflow, ui_get_text_widgets도 유사하게 구현
}
Map<String, dynamic> _buildWidgetTree(int maxDepth) {
final rootElement = WidgetsBinding.instance.renderViewElement;
if (rootElement == null) return {};
return _elementToMap(rootElement, 0, maxDepth);
}
Map<String, dynamic> _elementToMap(Element element, int depth, int maxDepth) {
final widget = element.widget;
final result = <String, dynamic>{
'type': widget.runtimeType.toString(),
};
if (depth < maxDepth) {
final children = <Map<String, dynamic>>[];
element.visitChildren((child) {
children.add(_elementToMap(child, depth + 1, maxDepth));
});
if (children.isNotEmpty) {
result['children'] = children;
}
}
return result;
}
사용 예시#
위젯 트리 확인#
Q: 현재 화면의 위젯 구조 보여줘
A: ui_get_widget_tree depth=3 실행
→ Scaffold > AppBar + ListView > ListTile items
특정 위젯 찾기#
Q: 텍스트 위젯들 찾아줘
A: ui_get_text_widgets 실행
→ 15개 Text 위젯 발견, 내용 및 스타일 정보
오버플로우 검사#
Q: 레이아웃 오버플로우 문제 있나요?
A: ui_find_overflow 실행
→ Row 위젯에서 RIGHT OVERFLOW 24px 발견
화면 정보 확인#
Q: 현재 디바이스 화면 크기는?
A: ui_get_screen_info 실행
→ 390x844, portrait, iOS, 3.0x pixel ratio
일반적인 문제 진단#
레이아웃 오버플로우#
1. ui_find_overflow로 문제 위젯 식별
2. 해당 위젯 경로 확인
3. constraints vs size 비교
4. Expanded, Flexible, SingleChildScrollView 적용 제안
위젯 찾기 실패#
1. ui_find_widgets type="TargetWidget"
2. 위젯 존재 여부 확인
3. 위젯 트리 경로 추적
4. 조건부 렌더링 로직 검토
UI 성능 문제#
1. ui_get_widget_tree로 깊이 확인
2. 불필요하게 깊은 중첩 식별
3. const 위젯 사용 여부 확인
4. 리빌드 빈도 분석 (@flutter-inspector-bloc 연계)
반응형 레이아웃#
1. ui_get_screen_info로 화면 크기 확인
2. 다양한 크기에서 ui_find_overflow 실행
3. MediaQuery 기반 조건 검토
관련 에이전트#
@flutter-inspector: 마스터 인스펙터@flutter-inspector-bloc: 상태와 UI 연결 확인@flutter-ui: UI 컴포넌트 구현 가이드