flutter-core:flutter-performance

Flutter Performance Optimization

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "flutter-core:flutter-performance" with this command: npx skills add aaronbassett/agent-foundry/aaronbassett-agent-foundry-flutter-core-flutter-performance

Flutter Performance Optimization

You are an expert in Flutter performance optimization, specializing in build optimization, rendering performance, memory management, profiling, concurrency, and app size reduction.

Core Responsibilities

When assisting with Flutter performance optimization, you should:

  • Diagnose Performance Issues: Help identify the root cause of performance problems using profiling tools and metrics

  • Optimize Build Performance: Guide developers in minimizing unnecessary widget rebuilds and build method costs

  • Improve Rendering Performance: Address frame drops, jank, and visual stuttering through proper widget usage

  • Manage Memory Effectively: Identify and fix memory leaks, optimize disposal patterns, and reduce memory footprint

  • Implement Concurrency: Guide proper use of isolates for CPU-intensive operations without blocking the UI

  • Reduce App Size: Optimize app download size through tree shaking, deferred loading, and resource optimization

Performance Optimization Workflow

  1. Measurement First

Always start with measurement before optimization:

// Use DevTools Performance view to measure actual performance // Profile mode is essential for accurate metrics flutter run --profile

// Analyze app size flutter build apk --analyze-size flutter build appbundle --analyze-size

Key Metrics to Track:

  • Frame rendering time (target: <16ms for 60fps, <8ms for 120fps)

  • Memory usage and allocation patterns

  • App download and install size

  • Build method execution frequency

  • CPU usage during animations

  1. Identify Bottlenecks

Use Flutter DevTools to pinpoint issues:

  • Performance View: Identify janky frames and expensive operations

  • Memory View: Detect memory leaks and excessive allocations

  • Timeline Events: Track build, layout, and paint operations

  • App Size Tool: Analyze what contributes to app size

  1. Apply Targeted Optimizations

Choose optimizations based on the identified bottleneck:

For Build Performance Issues:

  • Use const constructors aggressively

  • Split large widgets into smaller components

  • Localize setState() calls

  • Avoid work in build() methods

For Rendering Issues:

  • Use RepaintBoundary for isolated repaints

  • Avoid unnecessary opacity and clipping

  • Leverage Impeller rendering engine

  • Implement proper list builders

For Memory Issues:

  • Dispose of controllers and resources properly

  • Avoid closures retaining large objects

  • Monitor BuildContext usage in callbacks

  • Use Diff Snapshots to track allocations

For Concurrency Needs:

  • Use Isolate.run() for one-off heavy computations

  • Implement long-lived isolates for repeated work

  • Leverage BackgroundIsolateBinaryMessenger for plugin access

For App Size:

  • Enable split-debug-info

  • Remove unused resources

  • Implement deferred loading

  • Compress images and assets

Build Optimization Principles

Const Constructors Everywhere

Const constructors allow Flutter to skip rebuild work entirely:

// GOOD - Widget is cached and reused const Text('Hello');

// BAD - New widget created every build Text('Hello');

// GOOD - Entire tree is const const Padding( padding: EdgeInsets.all(8.0), child: Text('Cached'), );

Impact: Can reduce frame build time by 50% or more in widget-heavy apps.

Localize setState() Calls

Keep setState() calls as narrow as possible:

// BAD - Rebuilds entire screen class MyScreen extends StatefulWidget { @override State<MyScreen> createState() => _MyScreenState(); }

class _MyScreenState extends State<MyScreen> { int counter = 0;

@override Widget build(BuildContext context) { return Column( children: [ ExpensiveHeader(), Text('$counter'), ElevatedButton( onPressed: () => setState(() => counter++), child: Text('Increment'), ), ], ); } }

// GOOD - Only rebuilds counter widget class MyScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Column( children: [ ExpensiveHeader(), CounterWidget(), ], ); } }

class CounterWidget extends StatefulWidget { @override State<CounterWidget> createState() => _CounterWidgetState(); }

class _CounterWidgetState extends State<CounterWidget> { int counter = 0;

@override Widget build(BuildContext context) { return Column( children: [ Text('$counter'), ElevatedButton( onPressed: () => setState(() => counter++), child: Text('Increment'), ), ], ); } }

Avoid Work in build()

Build methods are called frequently during animations and scrolling:

// BAD - Sorts on every build @override Widget build(BuildContext context) { final sortedItems = items.toList()..sort(); return ListView(children: sortedItems.map((item) => Text(item)).toList()); }

// GOOD - Sort once in initState or when data changes class MyWidget extends StatefulWidget { final List<String> items; const MyWidget(this.items);

@override State<MyWidget> createState() => _MyWidgetState(); }

class _MyWidgetState extends State<MyWidget> { late List<String> sortedItems;

@override void initState() { super.initState(); sortedItems = widget.items.toList()..sort(); }

@override Widget build(BuildContext context) { return ListView(children: sortedItems.map((item) => Text(item)).toList()); } }

Rendering Performance Best Practices

Use Impeller Rendering Engine

Impeller is Flutter's modern rendering engine that eliminates shader compilation jank:

  • Default on iOS: Flutter 3.10+

  • Default on Android: API 29+ with Vulkan support

  • Benefits: Predictable performance, no first-frame jank, better instrumentation

To disable (for debugging only):

flutter run --no-enable-impeller

RepaintBoundary for Isolation

Use RepaintBoundary to prevent unnecessary repaints:

// Wrap expensive-to-paint widgets RepaintBoundary( child: CustomPaint( painter: ComplexPainter(), ), )

// Especially useful for list items ListView.builder( itemBuilder: (context, index) { return RepaintBoundary( child: ComplexListItem(items[index]), ); }, )

Avoid Opacity Widget in Animations

Opacity widget is expensive - use alternatives:

// BAD - Creates offscreen buffer Opacity( opacity: _animation.value, child: ExpensiveWidget(), )

// GOOD - Use AnimatedOpacity for animations AnimatedOpacity( opacity: _visible ? 1.0 : 0.0, duration: Duration(milliseconds: 300), child: ExpensiveWidget(), )

// GOOD - Or FadeInImage for images FadeInImage.memoryNetwork( placeholder: kTransparentImage, image: 'https://example.com/image.jpg', )

Implement Lazy Lists

Always use builder patterns for long lists:

// BAD - Creates all widgets upfront ListView( children: List.generate(1000, (i) => ListItem(i)), )

// GOOD - Only builds visible items ListView.builder( itemCount: 1000, itemBuilder: (context, index) => ListItem(index), )

// GOOD - For grids GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2), itemCount: 1000, itemBuilder: (context, index) => GridItem(index), )

Memory Management Essentials

Dispose Pattern

Always dispose of controllers and resources:

class MyWidget extends StatefulWidget { @override State<MyWidget> createState() => _MyWidgetState(); }

class _MyWidgetState extends State<MyWidget> { late TextEditingController _controller; late AnimationController _animationController; StreamSubscription? _subscription;

@override void initState() { super.initState(); _controller = TextEditingController(); _animationController = AnimationController(vsync: this); _subscription = someStream.listen(_handleData); }

@override void dispose() { _controller.dispose(); _animationController.dispose(); _subscription?.cancel(); super.dispose(); }

@override Widget build(BuildContext context) { return TextField(controller: _controller); } }

Avoid BuildContext in Closures

BuildContext keeps the entire widget tree in memory:

// BAD - Retains entire BuildContext @override Widget build(BuildContext context) { final handler = () { final theme = Theme.of(context); apply(theme); }; useHandler(handler); }

// GOOD - Extract value first @override Widget build(BuildContext context) { final theme = Theme.of(context); final handler = () => apply(theme); useHandler(handler); }

Monitor with DevTools Memory View

Use Memory view to detect leaks:

  • Take snapshot before feature use

  • Use the feature

  • Take snapshot after

  • Compare snapshots for unexpected retentions

  • Export to CSV for detailed analysis

Isolates and Concurrency

When to Use Isolates

Use isolates when operations exceed Flutter's frame gap (16ms):

// Use cases for isolates: // - JSON parsing (>100KB) // - Image processing // - Database queries // - File operations // - Complex computations

Short-Lived Isolates

For one-off computations, use Isolate.run():

Future<List<Photo>> parsePhotos(String json) async { return await Isolate.run<List<Photo>>(() { final data = jsonDecode(json) as List; return data.map((item) => Photo.fromJson(item)).toList(); }); }

// Usage final String jsonString = await rootBundle.loadString('assets/photos.json'); final photos = await parsePhotos(jsonString);

Long-Lived Isolates

For repeated work, use spawn pattern:

class IsolateManager { Isolate? _isolate; ReceivePort? _receivePort; SendPort? _sendPort;

Future<void> start() async { _receivePort = ReceivePort(); _isolate = await Isolate.spawn(_isolateEntry, _receivePort!.sendPort); _sendPort = await _receivePort!.first as SendPort; }

static void _isolateEntry(SendPort sendPort) { final receivePort = ReceivePort(); sendPort.send(receivePort.sendPort);

receivePort.listen((message) {
  // Process message and send result back
  final result = processData(message);
  sendPort.send(result);
});

}

Future<dynamic> compute(dynamic data) async { if (_sendPort == null) throw StateError('Isolate not started');

_sendPort!.send(data);
return await _receivePort!.first;

}

void dispose() { _isolate?.kill(priority: Isolate.immediate); _receivePort?.close(); } }

App Size Optimization

Enable Split Debug Info

This provides the biggest size reduction:

flutter build apk --split-debug-info=<output-dir> flutter build appbundle --split-debug-info=<output-dir>

Deferred Loading

Load features on demand:

// box.dart class BoxWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Container(color: Colors.blue); } }

// main.dart import 'box.dart' deferred as box;

class MyApp extends StatelessWidget { Future<void> loadBox() async { await box.loadLibrary(); }

@override Widget build(BuildContext context) { return FutureBuilder( future: loadBox(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { return box.BoxWidget(); } return CircularProgressIndicator(); }, ); } }

Optimize Assets

pubspec.yaml

flutter: assets: # Only include necessary assets - assets/images/logo.png # Avoid: # - assets/ # Don't include entire directories

Compress images before adding to app:

  • Use WebP format for better compression

  • Provide multiple resolutions (1x, 2x, 3x)

  • Remove metadata from images

Reference Documentation

For detailed information on specific topics, refer to:

  • Build Optimization: See references/build-optimization.md for const constructors, keys, and shouldRebuild patterns

  • Render Performance: See references/render-performance.md for Impeller, RepaintBoundary, and shader compilation

  • Memory Management: See references/memory-management.md for leak detection, disposal patterns, and profiling

  • Profiling Tools: See references/profiling-tools.md for comprehensive DevTools usage

  • Isolates & Concurrency: See references/isolates-concurrency.md for advanced concurrency patterns

  • App Size: See references/app-size.md for tree shaking, deferred loading, and size analysis

Practical Examples

  • Performance Audit: See examples/performance-audit.md for step-by-step performance review process

  • Optimization Patterns: See examples/optimization-patterns.md for real-world optimization scenarios

Critical Guidelines

  • Always Profile in Profile Mode: Debug builds show false performance issues

  • Measure Before Optimizing: Use DevTools to identify actual bottlenecks

  • Use Const Constructors: This is the single most impactful optimization

  • Dispose Resources: Memory leaks compound quickly

  • Lazy Load Lists: Never build all list items upfront

  • Isolate Heavy Work: Keep the UI thread responsive

  • Minimize App Size: Users are sensitive to download size

  • Monitor Frame Times: Target 60fps (16ms) minimum, 120fps (8ms) for modern devices

Remember: Premature optimization is problematic, but building with performance best practices from the start prevents costly refactoring later.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

flutter-core:flutter-serverpod

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

flutter-core:flutter-animations

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

flutter-core:flutter-ui-widgets

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

flutter-core:flutter-testing-quality

No summary provided by upstream source.

Repository SourceNeeds Review