Agent skill
performance-altrupets-monorepo
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/performance-altrupets-monorepo
SKILL.md
⚡ Skill: Performance Optimization
📋 Metadata
| Atributo | Valor |
|---|---|
| ID | flutter-performance |
| Nivel | 🔴 Avanzado |
| Versión | 1.0.0 |
| Keywords | performance, optimization, profiling, memory, rendering |
| Referencia | Flutter Performance Best Practices |
🔑 Keywords para Invocación
performanceoptimizationprofilingmemory-leakrendering-optimization@skill:performance
Ejemplos de Prompts
Optimiza el performance de la app
Reduce el rebuild y mejora el rendering
@skill:performance - Implementa mejores prácticas de performance
📖 Descripción
Performance Optimization cubre técnicas para mejorar velocidad, fluidez y eficiencia de apps Flutter: reducir rebuilds, optimizar listas, gestión de memoria, lazy loading, code splitting, image optimization y profiling.
⚠️ IMPORTANTE: Todos los comandos de este skill deben ejecutarse desde la raíz del proyecto (donde existe el directorio mobile/). El skill incluye verificaciones para asegurar que se está en el directorio correcto antes de ejecutar cualquier comando.
⚠️ IMPORTANTE: Todos los comandos de este skill deben ejecutarse desde la raíz del proyecto (donde existe el directorio mobile/). El skill incluye verificaciones para asegurar que se está en el directorio correcto antes de ejecutar cualquier comando.
✅ Cuándo Usar Este Skill
- App tiene lag o stuttering
- Tiempos de carga lentos
- Consumo excesivo de memoria
- Frame drops en animaciones
- Listas largas con scrolling lento
- Build times largos
❌ Cuándo NO Usar Este Skill
- Premature optimization en prototipos
- Performance ya es aceptable
🏗️ Estructura del Proyecto
lib/
├── core/
│ ├── performance/
│ │ ├── performance_monitor.dart
│ │ ├── memory_tracker.dart
│ │ └── build_tracker.dart
│ └── widgets/
│ ├── optimized/
│ │ ├── optimized_list_view.dart
│ │ ├── cached_network_image_wrapper.dart
│ │ └── sliver_performance_widget.dart
│ └── lazy/
│ └── lazy_indexed_stack.dart
└── main.dart
📦 Dependencias Requeridas
dependencies:
flutter:
sdk: flutter
# Image optimization
cached_network_image: ^3.3.0
flutter_cache_manager: ^3.3.1
# Performance monitoring
performance: ^0.2.0
# Utils
equatable: ^2.0.5
dev_dependencies:
# Performance testing
integration_test:
sdk: flutter
flutter_driver:
sdk: flutter
💻 Implementación
1. Reducir Rebuilds con const
// ❌ BAD: Widget se reconstruye en cada frame
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Text('Hello'),
);
}
}
// ✅ GOOD: Widget es const, no se reconstruye
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
@override
Widget build(BuildContext context) {
return const Text('Hello');
}
}
2. Usar Keys Correctamente
// ✅ GOOD: ValueKey para identificar widgets únicamente
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return ItemWidget(
key: ValueKey(item.id), // Ayuda a Flutter a identificar el widget
item: item,
);
},
)
3. Optimizar ListView con ListView.builder
// ❌ BAD: Crea todos los widgets a la vez
ListView(
children: items.map((item) => ItemWidget(item: item)).toList(),
)
// ✅ GOOD: Crea widgets solo cuando son visibles
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ItemWidget(item: items[index]);
},
)
// ✅ BETTER: Con separadores eficientes
ListView.separated(
itemCount: items.length,
itemBuilder: (context, index) => ItemWidget(item: items[index]),
separatorBuilder: (context, index) => const Divider(height: 1),
)
4. Usar RepaintBoundary
// Aislar widgets que se repintan frecuentemente
class AnimatedWidget extends StatefulWidget {
@override
_AnimatedWidgetState createState() => _AnimatedWidgetState();
}
class _AnimatedWidgetState extends State<AnimatedWidget>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
)..repeat();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
// Widget estático, no necesita repaint
const Text('Static Header'),
// Widget animado, aislado con RepaintBoundary
RepaintBoundary(
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform.rotate(
angle: _controller.value * 2 * 3.14159,
child: child,
);
},
child: const Icon(Icons.refresh, size: 100),
),
),
// Otro widget estático
const Text('Static Footer'),
],
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
5. Optimizar Imágenes
// lib/core/widgets/optimized/optimized_image.dart
import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
class OptimizedImage extends StatelessWidget {
final String imageUrl;
final double? width;
final double? height;
final BoxFit fit;
const OptimizedImage({
super.key,
required this.imageUrl,
this.width,
this.height,
this.fit = BoxFit.cover,
});
@override
Widget build(BuildContext context) {
return CachedNetworkImage(
imageUrl: imageUrl,
width: width,
height: height,
fit: fit,
// Progressive loading
progressIndicatorBuilder: (context, url, downloadProgress) {
return Center(
child: CircularProgressIndicator(
value: downloadProgress.progress,
),
);
},
// Error handling
errorWidget: (context, url, error) {
return const Icon(Icons.error);
},
// Memory cache
memCacheWidth: width?.toInt(),
memCacheHeight: height?.toInt(),
// Fade in animation
fadeInDuration: const Duration(milliseconds: 300),
);
}
}
// Uso
OptimizedImage(
imageUrl: 'https://example.com/image.jpg',
width: 100,
height: 100,
fit: BoxFit.cover,
)
6. Lazy Loading con AutomaticKeepAliveClientMixin
// Mantener estado de tabs sin reconstruir
class ProductsTab extends StatefulWidget {
const ProductsTab({super.key});
@override
State<ProductsTab> createState() => _ProductsTabState();
}
class _ProductsTabState extends State<ProductsTab>
with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true; // Mantener estado
@override
Widget build(BuildContext context) {
super.build(context); // IMPORTANTE: Llamar super.build
return ListView.builder(
itemCount: 100,
itemBuilder: (context, index) {
return ListTile(title: Text('Product $index'));
},
);
}
}
7. Optimizar Slivers
class OptimizedScrollView extends StatelessWidget {
const OptimizedScrollView({super.key});
@override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
// App bar que se colapsa
SliverAppBar(
expandedHeight: 200,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: const Text('Products'),
background: Image.network(
'https://example.com/header.jpg',
fit: BoxFit.cover,
),
),
),
// Lista optimizada con builder
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return ProductCard(index: index);
},
childCount: 1000,
),
),
// Grid optimizado
SliverGrid(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
return GridItem(index: index);
},
childCount: 100,
),
),
],
);
}
}
8. Evitar Funciones en build()
// ❌ BAD: Función se ejecuta en cada build
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Text(
_formatDate(DateTime.now()), // Se calcula en cada build
style: _getTextStyle(), // Se crea en cada build
);
}
String _formatDate(DateTime date) {
return '${date.day}/${date.month}/${date.year}';
}
TextStyle _getTextStyle() {
return const TextStyle(fontSize: 16, fontWeight: FontWeight.bold);
}
}
// ✅ GOOD: Valores se calculan una vez
class MyWidget extends StatelessWidget {
final String formattedDate;
static const textStyle = TextStyle(fontSize: 16, fontWeight: FontWeight.bold);
MyWidget({super.key}) : formattedDate = _formatDate(DateTime.now());
static String _formatDate(DateTime date) {
return '${date.day}/${date.month}/${date.year}';
}
@override
Widget build(BuildContext context) {
return Text(formattedDate, style: textStyle);
}
}
9. Usar Selector para Actualizaciones Específicas
// Con flutter_bloc
class ProductPriceText extends StatelessWidget {
const ProductPriceText({super.key});
@override
Widget build(BuildContext context) {
// Solo se reconstruye cuando el precio cambia
final price = context.select((ProductBloc bloc) => bloc.state.price);
return Text('\$$price');
}
}
// Con Provider
class ProductPriceText extends StatelessWidget {
const ProductPriceText({super.key});
@override
Widget build(BuildContext context) {
// Solo se reconstruye cuando el precio cambia
final price = context.select<ProductModel, double>((product) => product.price);
return Text('\$$price');
}
}
10. Lazy IndexedStack
// lib/core/widgets/lazy/lazy_indexed_stack.dart
class LazyIndexedStack extends StatefulWidget {
final int index;
final List<Widget> children;
const LazyIndexedStack({
super.key,
required this.index,
required this.children,
});
@override
State<LazyIndexedStack> createState() => _LazyIndexedStackState();
}
class _LazyIndexedStackState extends State<LazyIndexedStack> {
late List<bool> _activated;
@override
void initState() {
super.initState();
_activated = List.generate(widget.children.length, (i) => i == widget.index);
}
@override
void didUpdateWidget(LazyIndexedStack oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.index != widget.index) {
_activated[widget.index] = true;
}
}
@override
Widget build(BuildContext context) {
return IndexedStack(
index: widget.index,
children: List.generate(
widget.children.length,
(i) => _activated[i] ? widget.children[i] : const SizedBox.shrink(),
),
);
}
}
11. Performance Monitoring
// lib/core/performance/performance_monitor.dart
import 'package:flutter/scheduler.dart';
class PerformanceMonitor {
static void trackBuildTime(String widgetName, VoidCallback buildFunction) {
final stopwatch = Stopwatch()..start();
buildFunction();
stopwatch.stop();
if (stopwatch.elapsedMilliseconds > 16) { // 60 FPS = 16ms per frame
print('⚠️ Slow build: $widgetName took ${stopwatch.elapsedMilliseconds}ms');
}
}
static void trackFrameTime() {
SchedulerBinding.instance.addTimingsCallback((timings) {
for (final timing in timings) {
final buildTime = timing.buildDuration.inMilliseconds;
final rasterTime = timing.rasterDuration.inMilliseconds;
if (buildTime > 16 || rasterTime > 16) {
print('⚠️ Frame drop: Build=${buildTime}ms, Raster=${rasterTime}ms');
}
}
});
}
}
// Uso
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return PerformanceMonitor.trackBuildTime('MyWidget', () {
return Container(
// widget content
);
}) as Widget;
}
}
12. Code Splitting con Deferred Loading
// lib/features/settings/settings.dart
library settings;
export 'presentation/screens/settings_screen.dart';
export 'presentation/widgets/settings_list.dart';
// main.dart
import 'features/settings/settings.dart' deferred as settings;
// Cargar módulo cuando sea necesario
void navigateToSettings() async {
await settings.loadLibrary(); // Carga el código
Navigator.push(
context,
MaterialPageRoute(builder: (_) => settings.SettingsScreen()),
);
}
🎯 Mejores Prácticas
1. Profiling con DevTools
# Abrir Flutter DevTools
flutter pub global activate devtools
flutter pub global run devtools
# Verificar que estamos en la raíz del proyecto
if [ ! -d "mobile" ]; then
echo "Error: Ejecuta este comando desde la raíz del proyecto"
exit 1
fi
# Ejecutar app en profile mode
cd mobile
flutter run --profile
cd ..
# Performance overlay en código
void main() {
runApp(MyApp());
if (kProfileMode) {
WidgetsApp.debugShowWidgetInspector = true;
}
}
2. Build Mode Correcto
# Verificar que estamos en la raíz del proyecto
if [ ! -d "mobile" ]; then
echo "Error: Ejecuta este comando desde la raíz del proyecto"
exit 1
fi
# Debug (desarrollo) - Sin optimizaciones
cd mobile
flutter run
cd ..
# Profile (testing performance) - Optimizado pero con DevTools
cd mobile
flutter run --profile
cd ..
# Release (producción) - Totalmente optimizado
cd mobile
flutter run --release
cd ..
3. Avoid Doing Work in Build
// ❌ BAD
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Cálculos pesados aquí
final expensiveData = _performExpensiveCalculation();
return Text(expensiveData);
}
}
// ✅ GOOD
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late String _cachedData;
@override
void initState() {
super.initState();
_cachedData = _performExpensiveCalculation();
}
@override
Widget build(BuildContext context) {
return Text(_cachedData);
}
}
4. Dispose Correctly
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late AnimationController _controller;
late StreamSubscription _subscription;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
_subscription = someStream.listen((_) {});
}
@override
void dispose() {
_controller.dispose(); // ✅ Siempre dispose
_subscription.cancel(); // ✅ Siempre cancel
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container();
}
}
📊 Performance Checklist
- Usar
constconstructors donde sea posible - ListView.builder en lugar de ListView con children
- RepaintBoundary para widgets que se repintan frecuentemente
- Imágenes optimizadas y cacheadas
- Dispose de controllers, streams y subscriptions
- Lazy loading de tabs con AutomaticKeepAliveClientMixin
- Keys apropiadas en listas
- Evitar funciones anónimas en build()
- Code splitting con deferred loading
- Profiling regular con DevTools
📚 Recursos Adicionales
🔗 Skills Relacionados
- Clean Architecture
- Testing Strategy
Versión: 1.0.0 Última actualización: Diciembre 2025
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?