debug:flutter

📁 snakeo/claude-debug-and-refactor-skills-plugin 📅 Jan 19, 2026
28
总安装量
20
周安装量
#13405
全站排名
安装命令
npx skills add https://github.com/snakeo/claude-debug-and-refactor-skills-plugin --skill debug:flutter

Agent 安装分布

opencode 16
claude-code 16
codex 15
github-copilot 13
antigravity 11

Skill 文档

Flutter Debugging Guide

This skill provides a systematic approach to debugging Flutter applications, covering common error patterns, debugging tools, and best practices for efficient problem resolution.

Common Error Patterns

1. RenderFlex Overflow Error

Symptoms:

  • Yellow and black stripes appear in the UI indicating overflow area
  • Error message: “A RenderFlex overflowed by X pixels”

Causes:

  • Content exceeds available space in Row/Column
  • Fixed-size widgets in constrained containers
  • Text without proper overflow handling

Solutions:

// Solution 1: Wrap in SingleChildScrollView
SingleChildScrollView(
  child: Column(
    children: [...],
  ),
)

// Solution 2: Use Flexible or Expanded
Row(
  children: [
    Expanded(
      child: Text('Long text that might overflow...'),
    ),
  ],
)

// Solution 3: Handle text overflow explicitly
Text(
  'Long text...',
  overflow: TextOverflow.ellipsis,
  maxLines: 2,
)

2. setState() Called After Dispose

Symptoms:

  • Runtime error: “setState() called after dispose()”
  • App crashes after async operations complete

Causes:

  • Calling setState() after widget is removed from tree
  • Async operations completing after navigation away
  • Timer callbacks on disposed widgets

Solutions:

// Solution 1: Check mounted before setState
Future<void> fetchData() async {
  final data = await api.getData();
  if (mounted) {
    setState(() {
      _data = data;
    });
  }
}

// Solution 2: Cancel async operations in dispose
late final StreamSubscription _subscription;


void dispose() {
  _subscription.cancel();
  super.dispose();
}

// Solution 3: Use CancelableOperation
CancelableOperation<Data>? _operation;

Future<void> fetchData() async {
  _operation = CancelableOperation.fromFuture(api.getData());
  final data = await _operation!.value;
  if (mounted) {
    setState(() => _data = data);
  }
}


void dispose() {
  _operation?.cancel();
  super.dispose();
}

3. Null Check Operator Errors

Symptoms:

  • Error: “Null check operator used on a null value”
  • App crashes when accessing nullable values with !

Causes:

  • Using ! operator on null values
  • Not initializing late variables
  • Race conditions in async code

Solutions:

// Bad: Using ! without null check
final name = user!.name;

// Good: Use null-aware operators
final name = user?.name ?? 'Unknown';

// Good: Use if-null check
if (user != null) {
  final name = user.name;
}

// Good: Use pattern matching (Dart 3+)
if (user case final u?) {
  final name = u.name;
}

// For late initialization, consider nullable with check
String? _data;

String get data {
  if (_data == null) {
    throw StateError('Data not initialized');
  }
  return _data!;
}

4. Platform Channel Issues

Symptoms:

  • MissingPluginException
  • PlatformException with native code errors
  • Method channel not responding

Solutions:

// Solution 1: Ensure proper platform setup
// Run flutter clean && flutter pub get

// Solution 2: Check method channel registration
const platform = MethodChannel('com.example/channel');

try {
  final result = await platform.invokeMethod('methodName');
} on PlatformException catch (e) {
  debugPrint('Platform error: ${e.message}');
} on MissingPluginException {
  debugPrint('Plugin not registered for this platform');
}

// Solution 3: For plugins, ensure proper initialization
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // Initialize plugins here
  runApp(MyApp());
}

5. Build Context Errors

Symptoms:

  • “Looking up a deactivated widget’s ancestor is unsafe”
  • Navigator operations fail
  • Theme/MediaQuery not available

Causes:

  • Using context after widget disposal
  • Using context in initState before build
  • Storing context references

Solutions:

// Bad: Using context in initState

void initState() {
  super.initState();
  // Theme.of(context); // Error!
}

// Good: Use didChangeDependencies

void didChangeDependencies() {
  super.didChangeDependencies();
  final theme = Theme.of(context);
}

// Good: Use Builder for nested contexts
Scaffold(
  body: Builder(
    builder: (context) {
      // This context has access to Scaffold
      return ElevatedButton(
        onPressed: () {
          Scaffold.of(context).showSnackBar(...);
        },
        child: Text('Show Snackbar'),
      );
    },
  ),
)

// Good: Use GlobalKey for cross-widget access
final scaffoldKey = GlobalKey<ScaffoldState>();

6. Hot Reload Failures

Symptoms:

  • Changes not reflecting after save
  • App state lost unexpectedly
  • “Hot reload not available” message

Causes:

  • Changing app initialization code
  • Modifying const constructors
  • Native code changes
  • Global variable mutations

Solutions:

# Solution 1: Hot restart instead of hot reload
# Press 'R' (capital) in terminal or Shift+Cmd+\ in VS Code

# Solution 2: Full restart
flutter run --no-hot

# Solution 3: Clean build
flutter clean && flutter pub get && flutter run

Code patterns that require hot restart:

// Changes to main() require restart
void main() {
  runApp(MyApp()); // Modification here needs restart
}

// Changes to initState logic often need restart

void initState() {
  super.initState();
  _controller = AnimationController(...); // Changes here need restart
}

// Const constructor changes need restart
const MyWidget({super.key}); // Adding/removing const needs restart

7. Vertical Viewport Given Unbounded Height

Symptoms:

  • Error: “Vertical viewport was given unbounded height”
  • ListView inside Column fails

Solutions:

// Bad: ListView in Column without constraints
Column(
  children: [
    ListView(...), // Error!
  ],
)

// Good: Use Expanded
Column(
  children: [
    Expanded(
      child: ListView(...),
    ),
  ],
)

// Good: Use shrinkWrap (for small lists only)
Column(
  children: [
    ListView(
      shrinkWrap: true,
      physics: NeverScrollableScrollPhysics(),
      children: [...],
    ),
  ],
)

// Good: Use SizedBox with fixed height
Column(
  children: [
    SizedBox(
      height: 200,
      child: ListView(...),
    ),
  ],
)

8. RenderBox Was Not Laid Out

Symptoms:

  • Error: “RenderBox was not laid out”
  • Widget fails to render

Solutions:

// Ensure parent provides constraints
SizedBox(
  width: 200,
  height: 200,
  child: CustomPaint(...),
)

// For intrinsic sizing
IntrinsicHeight(
  child: Row(
    children: [
      Container(color: Colors.red),
      Container(color: Colors.blue),
    ],
  ),
)

9. Incorrect Use of ParentDataWidget

Symptoms:

  • Error: “Incorrect use of ParentDataWidget”
  • Positioned/Expanded used incorrectly

Solutions:

// Bad: Positioned outside Stack
Column(
  children: [
    Positioned(...), // Error!
  ],
)

// Good: Positioned inside Stack
Stack(
  children: [
    Positioned(
      top: 10,
      left: 10,
      child: Text('Hello'),
    ),
  ],
)

// Bad: Expanded outside Flex widget
Container(
  child: Expanded(...), // Error!
)

// Good: Expanded inside Row/Column
Row(
  children: [
    Expanded(child: Text('Hello')),
  ],
)

10. Red/Grey Screen of Death

Symptoms:

  • Red screen in debug/profile mode
  • Grey screen in release mode
  • App appears frozen

Causes:

  • Uncaught exceptions
  • Rendering errors
  • Failed assertions

Solutions:

// Global error handling
void main() {
  FlutterError.onError = (details) {
    FlutterError.presentError(details);
    // Log to crash reporting service
    crashReporter.recordFlutterError(details);
  };

  PlatformDispatcher.instance.onError = (error, stack) {
    // Handle async errors
    crashReporter.recordError(error, stack);
    return true;
  };

  runApp(MyApp());
}

// Custom error widget
class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    ErrorWidget.builder = (FlutterErrorDetails details) {
      return CustomErrorWidget(details: details);
    };
    return MaterialApp(...);
  }
}

Debugging Tools

Flutter DevTools

The comprehensive suite for Flutter debugging:

# Launch DevTools
flutter pub global activate devtools
flutter pub global run devtools

# Or access via VS Code Flutter extension

Key DevTools Features:

  • Widget Inspector: Examine widget tree, properties, and render objects
  • Performance View: Analyze frame rendering and jank
  • Memory View: Track allocations and detect memory leaks
  • Network View: Monitor HTTP requests
  • Logging View: View all debug output
  • CPU Profiler: Identify performance bottlenecks

flutter doctor

Diagnose environment issues:

# Full diagnostic
flutter doctor -v

# Check specific issues
flutter doctor --android-licenses

# Common fixes
flutter clean
flutter pub get
flutter pub upgrade

Dart DevTools Debugger

// Set breakpoints in code
debugger(when: condition);

// Conditional breakpoints in IDE
// Right-click line number > Add Conditional Breakpoint

Logging Best Practices

// Basic logging
print('Debug message'); // stdout

// Better: debugPrint for large outputs (prevents dropped logs)
debugPrint('Large debug output...');

// Best: dart:developer log for granular control
import 'dart:developer';

log(
  'User action',
  name: 'UserFlow',
  error: exception,
  stackTrace: stackTrace,
);

// Conditional logging
assert(() {
  debugPrint('Only in debug mode');
  return true;
}());

// Using kDebugMode
import 'package:flutter/foundation.dart';

if (kDebugMode) {
  print('Debug only');
}

Flutter Inspector (Widget Inspector)

// Enable debug painting
import 'package:flutter/rendering.dart';

debugPaintSizeEnabled = true;
debugPaintBaselinesEnabled = true;
debugPaintLayerBordersEnabled = true;
debugPaintPointersEnabled = true;

// In widget
debugPrint(context.widget.toStringDeep());

The Four Phases of Flutter Debugging

Phase 1: Identify the Error Type

Categorize the error:

  1. Compile-time errors: Syntax, type errors (red squiggles)
  2. Runtime errors: Exceptions during execution
  3. Layout errors: RenderFlex overflow, unbounded constraints
  4. State errors: setState after dispose, inconsistent state
  5. Platform errors: Native plugin issues, permissions
  6. Performance issues: Jank, memory leaks, slow frames
# Get detailed error information
flutter analyze
flutter test --reporter expanded

Phase 2: Reproduce and Isolate

// Create minimal reproduction
class DebugWidget extends StatelessWidget {
  
  Widget build(BuildContext context) {
    // Isolate the problematic widget
    return Container(
      child: ProblematicWidget(),
    );
  }
}

// Use assertions to validate state
assert(data != null, 'Data should not be null at this point');
assert(index >= 0 && index < list.length, 'Index out of bounds: $index');

Phase 3: Debug and Fix

// Add strategic logging
void processData() {
  debugPrint('processData called with: $data');

  try {
    final result = transform(data);
    debugPrint('Transform result: $result');
  } catch (e, stack) {
    debugPrint('Transform failed: $e');
    debugPrint('Stack trace: $stack');
    rethrow;
  }
}

// Use DevTools breakpoints
// Set breakpoints at:
// - Method entry points
// - Before suspected failure points
// - In catch blocks

Phase 4: Verify and Prevent

// Add tests for the fix
testWidgets('Widget handles null data gracefully', (tester) async {
  await tester.pumpWidget(
    MaterialApp(
      home: MyWidget(data: null),
    ),
  );

  expect(find.text('No data'), findsOneWidget);
  expect(tester.takeException(), isNull);
});

// Add defensive coding
Widget build(BuildContext context) {
  if (data == null) {
    return const EmptyState();
  }
  return DataDisplay(data: data!);
}

Quick Reference Commands

Diagnostics

# Environment check
flutter doctor -v

# Analyze code for issues
flutter analyze

# Check for outdated packages
flutter pub outdated

# Upgrade packages
flutter pub upgrade --major-versions

Testing

# Run all tests
flutter test

# Run specific test file
flutter test test/widget_test.dart

# Run with coverage
flutter test --coverage

# Run integration tests
flutter test integration_test/

Build and Run

# Debug mode (default)
flutter run

# Profile mode (for performance debugging)
flutter run --profile

# Release mode
flutter run --release

# Specific device
flutter run -d <device_id>

# List devices
flutter devices

Clean and Reset

# Clean build artifacts
flutter clean

# Get dependencies
flutter pub get

# Reset iOS pods
cd ios && pod deintegrate && pod install && cd ..

# Reset Android
cd android && ./gradlew clean && cd ..

DevTools

# Install DevTools globally
flutter pub global activate devtools

# Run DevTools
flutter pub global run devtools

# Or use dart devtools
dart devtools

Performance Debugging

Identify Jank

// Enable performance overlay
MaterialApp(
  showPerformanceOverlay: true,
  ...
)

// Or toggle in DevTools
// Performance > Show Performance Overlay

Common Performance Issues

// Bad: Building expensive widgets in build()

Widget build(BuildContext context) {
  final expensiveData = computeExpensiveData(); // Called every rebuild!
  return ExpensiveWidget(data: expensiveData);
}

// Good: Cache expensive computations
late final expensiveData = computeExpensiveData();

// Good: Use const constructors
const MyWidget(key: Key('my-widget'));

// Good: Use RepaintBoundary for isolated repaints
RepaintBoundary(
  child: ExpensiveAnimatedWidget(),
)

Memory Debugging

// Check for leaks in DevTools Memory view

// Common leak patterns:
// 1. Streams not disposed

void dispose() {
  _streamSubscription.cancel();
  super.dispose();
}

// 2. Controllers not disposed

void dispose() {
  _textController.dispose();
  _animationController.dispose();
  super.dispose();
}

// 3. Listeners not removed

void dispose() {
  _focusNode.removeListener(_onFocusChange);
  _focusNode.dispose();
  super.dispose();
}

State Management Debugging

Provider/Riverpod

// Debug Provider rebuilds
Consumer<MyModel>(
  builder: (context, model, child) {
    debugPrint('Consumer rebuilding: ${model.value}');
    return Text(model.value);
  },
)

// Use ProviderScope observers
ProviderScope(
  observers: [DebugProviderObserver()],
  child: MyApp(),
)

class DebugProviderObserver extends ProviderObserver {
  
  void didUpdateProvider(
    ProviderBase provider,
    Object? previousValue,
    Object? newValue,
    ProviderContainer container,
  ) {
    debugPrint('Provider ${provider.name}: $previousValue -> $newValue');
  }
}

Bloc/Cubit

// Enable Bloc observer
Bloc.observer = DebugBlocObserver();

class DebugBlocObserver extends BlocObserver {
  
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    debugPrint('${bloc.runtimeType} $change');
  }

  
  void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
    debugPrint('${bloc.runtimeType} $error $stackTrace');
    super.onError(bloc, error, stackTrace);
  }
}

Platform-Specific Debugging

Android

# View Android logs
flutter logs

# Or use adb
adb logcat | grep flutter

# Debug native crashes
adb logcat -s AndroidRuntime:E

iOS

# View iOS logs
flutter logs

# Open Xcode console
open ios/Runner.xcworkspace

# Check crash logs
# Xcode > Window > Devices and Simulators > View Device Logs

Web

// Use browser DevTools console
import 'dart:html' as html;

html.window.console.log('Web debug message');

// Check for CORS issues in Network tab
// Check for CSP issues in Console

Additional Resources

For detailed documentation: