Skip to main content
Glama
improvementSuggester.js11.3 kB
import { analyzeCodeStructure } from '../utils/codeAnalyzer.js'; import { getFlutterPatterns } from '../validators/patterns.js'; export async function suggestImprovements(args) { const { code, focusArea = 'all' } = args; const improvements = { performance: [], accessibility: [], maintainability: [], architecture: [], patterns: [], }; try { const codeStructure = analyzeCodeStructure(code); if (focusArea === 'all' || focusArea === 'performance') { improvements.performance = suggestPerformanceImprovements(code, codeStructure); } if (focusArea === 'all' || focusArea === 'accessibility') { improvements.accessibility = suggestAccessibilityImprovements(code, codeStructure); } if (focusArea === 'all' || focusArea === 'maintainability') { improvements.maintainability = suggestMaintainabilityImprovements(code, codeStructure); } improvements.architecture = suggestArchitecturalImprovements(code, codeStructure); improvements.patterns = suggestPatternImprovements(code, codeStructure); const prioritizedImprovements = prioritizeImprovements(improvements); const implementationGuide = generateImplementationGuide(prioritizedImprovements); return { content: [ { type: 'text', text: JSON.stringify({ improvements, prioritized: prioritizedImprovements, implementationGuide, estimatedImpact: calculateImpact(improvements), }, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error suggesting improvements: ${error.message}`, }, ], }; } } function suggestPerformanceImprovements(code, structure) { const suggestions = []; if (!code.includes('const') && structure.hasStatelessWidgets) { suggestions.push({ title: 'Use const constructors', description: 'Add const keyword to improve widget rebuild performance', example: 'const Text("Hello") instead of Text("Hello")', impact: 'high', difficulty: 'easy', }); } if (code.includes('setState') && structure.stateUpdateCount > 2) { suggestions.push({ title: 'Optimize setState usage', description: 'Consider using ValueNotifier or state management for frequent updates', example: 'ValueNotifier<int> counter = ValueNotifier(0);', impact: 'high', difficulty: 'medium', }); } if (code.includes('ListView') && !code.includes('.builder')) { suggestions.push({ title: 'Use ListView.builder for dynamic lists', description: 'ListView.builder provides better performance for large lists', example: 'ListView.builder(itemCount: items.length, itemBuilder: (context, index) => ...)', impact: 'high', difficulty: 'easy', }); } if (structure.widgetTreeDepth > 6) { suggestions.push({ title: 'Reduce widget tree depth', description: 'Extract nested widgets into separate components', impact: 'medium', difficulty: 'medium', }); } if (code.includes('AnimatedContainer') || code.includes('AnimatedOpacity')) { suggestions.push({ title: 'Consider implicit animations performance', description: 'For complex animations, use AnimationController for better control', impact: 'medium', difficulty: 'hard', }); } return suggestions; } function suggestAccessibilityImprovements(code, structure) { const suggestions = []; if (!code.includes('Semantics') && structure.hasInteractiveWidgets) { suggestions.push({ title: 'Add Semantics widgets', description: 'Wrap interactive elements with Semantics for screen readers', example: 'Semantics(label: "Submit button", child: ElevatedButton(...))', impact: 'high', difficulty: 'easy', }); } if (code.includes('Image') && !code.includes('semanticLabel')) { suggestions.push({ title: 'Add semantic labels to images', description: 'Provide descriptions for images to support screen readers', example: 'Image.asset("path", semanticLabel: "Company logo")', impact: 'high', difficulty: 'easy', }); } if (code.includes('Color(') && !code.includes('MaterialColor')) { suggestions.push({ title: 'Ensure color contrast compliance', description: 'Use theme colors or verify WCAG AA contrast ratios', impact: 'medium', difficulty: 'medium', }); } if (!code.includes('textScaleFactor') && structure.hasTextWidgets) { suggestions.push({ title: 'Support dynamic text scaling', description: 'Ensure text scales properly with system settings', impact: 'medium', difficulty: 'easy', }); } return suggestions; } function suggestMaintainabilityImprovements(code, structure) { const suggestions = []; if (structure.methodLength > 50) { suggestions.push({ title: 'Break down large methods', description: 'Extract logic into smaller, focused methods', impact: 'high', difficulty: 'medium', }); } if (!code.includes('typedef') && structure.hasComplexCallbacks) { suggestions.push({ title: 'Use typedefs for callbacks', description: 'Define type aliases for complex function signatures', example: 'typedef OnItemSelected = void Function(String id);', impact: 'medium', difficulty: 'easy', }); } if (structure.duplicatedCode.length > 0) { suggestions.push({ title: 'Extract duplicated code', description: 'Create reusable widgets or methods for repeated code', duplicates: structure.duplicatedCode, impact: 'high', difficulty: 'medium', }); } if (!code.includes('assert') && structure.hasPublicMethods) { suggestions.push({ title: 'Add debug assertions', description: 'Use assert statements to catch errors during development', example: 'assert(value != null, "Value must not be null");', impact: 'low', difficulty: 'easy', }); } return suggestions; } function suggestArchitecturalImprovements(code, structure) { const suggestions = []; if (structure.hasBusinessLogicInUI) { suggestions.push({ title: 'Separate business logic from UI', description: 'Move business logic to controllers or services', pattern: 'Consider MVC, MVP, or BLoC pattern', impact: 'high', difficulty: 'hard', }); } if (!structure.hasProperSeparation) { suggestions.push({ title: 'Implement proper layer separation', description: 'Organize code into presentation, domain, and data layers', impact: 'high', difficulty: 'hard', }); } if (structure.tightCoupling > 0.7) { suggestions.push({ title: 'Reduce coupling between components', description: 'Use dependency injection or interfaces', impact: 'medium', difficulty: 'medium', }); } return suggestions; } function suggestPatternImprovements(code, structure) { const suggestions = []; const patterns = getFlutterPatterns(); if (!code.includes('Repository') && structure.hasDataFetching) { suggestions.push({ title: 'Implement Repository pattern', description: 'Abstract data sources behind a repository interface', pattern: patterns.repository, impact: 'medium', difficulty: 'medium', }); } if (structure.hasStateManagement && !usesKnownStateManagement(code)) { suggestions.push({ title: 'Use established state management', description: 'Consider Provider, Riverpod, or BLoC for state management', impact: 'high', difficulty: 'medium', }); } if (code.includes('Factory') || structure.hasComplexObjectCreation) { suggestions.push({ title: 'Apply Factory pattern correctly', description: 'Use factory constructors or factory methods appropriately', pattern: patterns.factory, impact: 'low', difficulty: 'easy', }); } return suggestions; } function usesKnownStateManagement(code) { const stateManagementKeywords = [ 'Provider', 'Riverpod', 'BlocProvider', 'GetX', 'MobX', 'Redux', 'StateNotifier', 'ChangeNotifier' ]; return stateManagementKeywords.some(keyword => code.includes(keyword)); } function prioritizeImprovements(improvements) { const allImprovements = []; Object.entries(improvements).forEach(([category, items]) => { items.forEach(item => { allImprovements.push({ ...item, category, score: calculatePriorityScore(item), }); }); }); return allImprovements .sort((a, b) => b.score - a.score) .slice(0, 10); } function calculatePriorityScore(improvement) { const impactScores = { high: 3, medium: 2, low: 1 }; const difficultyScores = { easy: 3, medium: 2, hard: 1 }; const impact = impactScores[improvement.impact] || 0; const difficulty = difficultyScores[improvement.difficulty] || 0; return impact * difficulty; } function generateImplementationGuide(improvements) { const guide = { quickWins: [], mediumEffort: [], majorRefactoring: [], }; improvements.forEach(improvement => { if (improvement.difficulty === 'easy' && improvement.impact === 'high') { guide.quickWins.push({ title: improvement.title, steps: generateSteps(improvement), }); } else if (improvement.difficulty === 'medium') { guide.mediumEffort.push({ title: improvement.title, steps: generateSteps(improvement), }); } else if (improvement.difficulty === 'hard') { guide.majorRefactoring.push({ title: improvement.title, steps: generateSteps(improvement), }); } }); return guide; } function generateSteps(improvement) { const steps = []; switch (improvement.title) { case 'Use const constructors': steps.push('Identify stateless widgets with fixed content'); steps.push('Add const keyword before constructor calls'); steps.push('Run flutter analyze to verify changes'); break; case 'Add Semantics widgets': steps.push('Identify all interactive widgets'); steps.push('Wrap each with appropriate Semantics widget'); steps.push('Add meaningful labels and hints'); steps.push('Test with screen reader'); break; default: steps.push('Review current implementation'); steps.push('Plan refactoring approach'); steps.push('Implement changes incrementally'); steps.push('Test thoroughly'); } return steps; } function calculateImpact(improvements) { const counts = { total: 0, highImpact: 0, implemented: 0, }; Object.values(improvements).forEach(categoryImprovements => { categoryImprovements.forEach(improvement => { counts.total++; if (improvement.impact === 'high') { counts.highImpact++; } }); }); return { potentialImprovement: counts.highImpact > 5 ? 'Significant' : 'Moderate', effortRequired: counts.total > 10 ? 'High' : 'Medium', recommendedApproach: counts.highImpact > 3 ? 'Prioritize high-impact changes' : 'Incremental improvements', }; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dvillegastech/flutter_mcp_2'

If you have feedback or need assistance with the MCP directory API, please join our Discord server