flutter_project_structure 2.0.4 copy "flutter_project_structure: ^2.0.4" to clipboard
flutter_project_structure: ^2.0.4 copied to clipboard

Save 90% of AI tokens. Analyze Flutter/Dart project structure, generate AI-friendly context (CLAUDE.md, .ai-context/) and MCP server for live querying.

example/main.dart

// example/main.dart
import 'dart:io';

import 'package:flutter_project_structure/flutter_project_structure.dart';
import 'package:path/path.dart' as path;

void main() async {
  print('Flutter Project Structure v2.0.4 Example\n');

  // Create a sample project structure
  final projectDir = await createSampleProject();

  // 1. The recommended way: generate() does everything in one pass
  //    - Injects path comments into every Dart file
  //    - Writes project_structure.md
  //    - Returns ProjectContext for generators
  print('1. Full analysis (path comments + project_structure.md + context):');
  fullAnalysis(projectDir);

  // 2. Use the returned context to generate AI outputs
  print('\n2. Generate AI context files:');
  generateAiOutputs(projectDir);

  // 3. Read-only analysis (no file modification) — for CI/CD or previews
  print('\n3. Read-only analysis:');
  readOnlyAnalysis(projectDir);

  // Clean up
  projectDir.deleteSync(recursive: true);
}

/// The recommended pattern: generate() injects path comments, writes
/// project_structure.md, and returns a ProjectContext — all in one pass.
void fullAnalysis(Directory projectDir) {
  final structure = FlutterProjectStructure(
    rootDir: path.join(projectDir.path, 'lib'),
    outputFile: path.join(projectDir.path, 'project_structure.md'),
  );

  // One call does it all
  final context = structure.generate();
  if (context == null) {
    print('Error: could not analyze project.');
    return;
  }

  print('Project type: ${context.projectTypeDetector?.projectType}');
  print('Total files: ${context.fileStatistics.totalFiles}');
  print('Total lines: ${context.fileStatistics.totalLines}');

  final frameworks = context.frameworkDetector?.detectedFrameworks;
  if (frameworks != null && frameworks.isNotEmpty) {
    for (final info in frameworks.values) {
      print('Framework: ${info.name} '
          '(pubspec: ${info.inPubspec}, '
          'files: ${info.fileEvidence.length})');
    }
  }

  final layers = context.architectureAnalyzer?.layerFiles;
  if (layers != null && layers.isNotEmpty) {
    for (final entry in layers.entries) {
      print('Layer: ${entry.key} (${entry.value.length} files)');
    }
  }

  final aggregator = context.metricsAggregator;
  if (aggregator != null) {
    print('Total classes: ${aggregator.totalClasses}');
    print('Total methods: ${aggregator.totalMethods}');
    print('Average LOC/file: ${aggregator.averageLoc.toStringAsFixed(1)}');
  }
}

/// Use the ProjectContext from generate() to create AI outputs.
/// This avoids scanning the project twice.
void generateAiOutputs(Directory projectDir) {
  final structure = FlutterProjectStructure(
    rootDir: path.join(projectDir.path, 'lib'),
    outputFile: path.join(projectDir.path, 'project_structure.md'),
  );

  // generate() returns the context — use it for all generators
  final context = structure.generate();
  if (context == null) return;

  // Generate CLAUDE.md
  final claudePath = path.join(projectDir.path, 'CLAUDE.md');
  ClaudeMdGenerator(context).generate(outputPath: claudePath);
  print('Generated CLAUDE.md at $claudePath');

  // Generate .ai-context/ JSON files
  final contextDir = path.join(projectDir.path, '.ai-context');
  AiContextGenerator(context).generate(outputDir: contextDir);
  print('Generated .ai-context/ at $contextDir');

  // List generated files
  final dir = Directory(contextDir);
  if (dir.existsSync()) {
    for (final file in dir.listSync()) {
      print('  - ${path.basename(file.path)}');
    }
  }

  // Show CLAUDE.md preview
  final content = File(claudePath).readAsStringSync();
  final preview =
      content.length > 500 ? '${content.substring(0, 500)}...' : content;
  print('\nCLAUDE.md preview:\n$preview');
}

/// Read-only analysis: no path comments, no file writes.
/// Useful for CI/CD, previews, or custom tooling.
void readOnlyAnalysis(Directory projectDir) {
  final structure = FlutterProjectStructure(
    rootDir: path.join(projectDir.path, 'lib'),
  );

  // runAnalysis() never modifies files
  final context = structure.runAnalysis();
  if (context == null) {
    print('Error: could not analyze project.');
    return;
  }

  print('Project type: ${context.projectTypeDetector?.projectType}');
  print('Total files: ${context.fileStatistics.totalFiles}');

  // Or get just the markdown string without writing to disk
  final markdown = structure.generateMarkdown();
  if (markdown != null) {
    print('Markdown length: ${markdown.length} characters');
  }
}

Future<Directory> createSampleProject() async {
  final projectDir = Directory.systemTemp
      .createTempSync('flutter_project_structure_example_');

  // Create pubspec.yaml with some frameworks
  File(path.join(projectDir.path, 'pubspec.yaml')).writeAsStringSync('''
name: example_app
version: 1.0.0
environment:
  sdk: '>=3.0.0 <4.0.0'
dependencies:
  flutter_bloc: ^8.0.0
  dio: ^5.0.0
  go_router: ^14.0.0
''');

  // Create lib directory with sample structure
  final libDir = Directory(path.join(projectDir.path, 'lib'))..createSync();
  File(path.join(libDir.path, 'main.dart')).writeAsStringSync('''
import 'package:flutter/material.dart';
import 'widgets/button.dart';

// TODO: Implement app initialization
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: CustomButton(),
        ),
      ),
    );
  }
}
''');

  // Environment entry points
  File(path.join(libDir.path, 'main_development.dart')).writeAsStringSync('''
import 'main.dart' as app;
void main() => app.main();
''');

  final widgetsDir = Directory(path.join(libDir.path, 'widgets'))..createSync();
  File(path.join(widgetsDir.path, 'button.dart')).writeAsStringSync('''
import 'package:flutter/material.dart';

// FIXME: Implement proper styling
class CustomButton extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {},
      child: Text('Click me'),
    );
  }
}
''');

  final modelsDir = Directory(path.join(libDir.path, 'models'))..createSync();
  File(path.join(modelsDir.path, 'user_model.dart')).writeAsStringSync('''
class UserModel {
  final String name;
  final String email;

  UserModel({required this.name, required this.email});
}
''');

  final servicesDir = Directory(path.join(libDir.path, 'services'))
    ..createSync();
  File(path.join(servicesDir.path, 'api_service.dart')).writeAsStringSync('''
import 'package:dio/dio.dart';

class ApiService {
  final Dio _dio = Dio();

  Future<dynamic> fetchData(String url) async {
    final response = await _dio.get(url);
    return response.data;
  }
}
''');

  print('Sample project created at: ${projectDir.path}');
  return projectDir;
}
2
likes
160
points
231
downloads

Documentation

API reference

Publisher

verified publishermsishamim.com

Weekly Downloads

Save 90% of AI tokens. Analyze Flutter/Dart project structure, generate AI-friendly context (CLAUDE.md, .ai-context/) and MCP server for live querying.

Repository (GitHub)
View/report issues

Topics

#flutter #dart #ai #code-analysis #mcp

License

MIT (license)

Dependencies

analyzer, args, mcp_dart, path, watcher, yaml

More

Packages that depend on flutter_project_structure