Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions skills/flutter-components/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Stitch to Flutter Skill

## Install

```bash
npx add-skill google-labs-code/stitch-skills --skill flutter:components --global
```

## Example Prompt

```text
Convert my Landing Page screen in my Podcast Stitch Project to a Flutter Page.
```

## Skill Structure

This repository follows the **Agent Skills** open standard. Each skill is self-contained with its own logic, validation scripts, and design tokens.

```text
skills/flutter-components/
├── SKILL.md — Core instructions & workflow
├── package.json — Validator dependencies
├── scripts/ — Networking & AST validation
├── resources/ — Style guides & API references
└── examples/ — Gold-standard code samples
```

## How it Works

When activated, the agent follows a high-fidelity engineering pipeline:

1. **Retrieval**: Uses a system-level `curl` script to bypass TLS/SNI issues on Google Cloud Storage.
2. **Mapping**: Cross-references Stitch metadata with the local `style-guide.json` to ensure token consistency.
3. **Generation**: Scaffolds components using a strict Atomic Design pattern.
4. **Validation**: Runs an automated AST check using `@swc/core` to prevent hardcoded hex values or missing interfaces.
5. **Audit**: Performs a final self-correction check against a 11-point architecture checklist.
44 changes: 44 additions & 0 deletions skills/flutter-components/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
name: flutter:components
description: Converts Stitch designs into a flutter Screen/Widget using system-level networking and logic-based validation.
allowed-tools:
- "stitch*:*"
- "run_command"
- "view_file"
- "write_to_file"
- "read_url_content"
---

# Stitch to Flutter Components

You are a Flutter frontend engineer focused on transforming designs into clean Flutter & Dart code. You follow a Single-file approach and use automated tools to ensure code quality.

## Retrieval and networking
1. **Namespace discovery**: Run `list_tools` to find the Stitch MCP prefix. Use this prefix (e.g., `stitch:`) for all subsequent calls.
2. **Metadata fetch**: Call `[prefix]:get_screen` to retrieve the design JSON.
3. **High-reliability download**: Internal AI fetch tools can sometimes fail on specific domains.
- Use the `run_command` tool to run: `bash scripts/fetch-stitch.sh "[htmlCode.downloadUrl]" "temp/source.html"`.
- This script handles the necessary redirects and security handshakes.
4. **Visual audit**: Check `screenshot.downloadUrl` to confirm the design intent and layout details.

## Architectural rules
* **Single file**: Generate a single .dart file for the component. This file should always be a `StatefulWidget`.
* **Logic isolation**: Logic should be encapsulated in its own functions with proper comments within the State class.
* **Project specific**: Focus on the target project's needs and constraints. Ensure the code is production-ready for the Flutter SDK.
* **Style mapping**:
* Extract the `tailwind.config` from the HTML `<head>`.
* Map these styles to Flutter's `ThemeData` or custom constants.
* Use Material 3 components by default.

## Execution steps
1. **Environment setup**: If `node_modules` is missing in the skill directory, run `npm install` to enable validation tools.
2. **Data layer**: Create `lib/component.dart` based on the design content.
3. **Component drafting**: Use `resources/component-template.dart` as a base. Find and replace all instances of `StitchPage` and `StitchPageState` with the actual name of your component.
4. **Application wiring**: Update the project entry point (like `main.dart`) to render the new component.
5. **Quality check**:
* Run `npm run validate <file_path>` for each component.
* Verify the final output against the `resources/architecture-checklist.md`.

## Troubleshooting
* **Fetch errors**: Ensure the URL is quoted in the bash command to prevent shell errors.
* **Validation errors**: Review the validation report and fix any missing constructors or hardcoded hex colors.
12 changes: 12 additions & 0 deletions skills/flutter-components/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "flutter-components",
"version": "1.0.0",
"description": "Validation for Flutter components",
"type": "module",
"scripts": {
"validate": "node scripts/validate.js"
},
"engines": {
"node": ">=18.0.0"
}
}
18 changes: 18 additions & 0 deletions skills/flutter-components/resources/architecture-checklist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Flutter Component Architecture Checklist

## State Management
- [ ] Component extends `StatefulWidget`.
- [ ] Logic is isolated into helper methods within the `State` class.
- [ ] Proper use of `setState` for UI updates.

## UI & Styling
- [ ] No hardcoded hex color codes (use `Theme.of(context)` where possible).
- [ ] Responsive layout using `LayoutBuilder` or `MediaQuery` if needed.
- [ ] Material 3 design principles followed.
- [ ] Typography extracted from Design JSON.

## Code Quality
- [ ] All TODOs resolved.
- [ ] Proper indentation and formatting.
- [ ] Public constructor with optional parameters.
- [ ] No unused imports.
22 changes: 22 additions & 0 deletions skills/flutter-components/resources/component-template.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import 'package:flutter/material.dart';

class StitchPage extends StatefulWidget {
const StitchPage({super.key});

@override
State<StitchPage> createState() => _StitchPageState();
}

class _StitchPageState extends State<StitchPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Stitch Component'),
),
body: const Center(
child: Text('Generate content here'),
),
);
}
}
30 changes: 30 additions & 0 deletions skills/flutter-components/scripts/fetch-stitch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

URL=$1
OUTPUT=$2
if [ -z "$URL" ] || [ -z "$OUTPUT" ]; then
echo "Usage: $0 <url> <output_path>"
exit 1
fi
echo "Initiating high-reliability fetch for Stitch HTML..."
curl -L -f -sS --connect-timeout 10 --compressed "$URL" -o "$OUTPUT"
if [ $? -eq 0 ]; then
echo "✅ Successfully retrieved HTML at: $OUTPUT"
exit 0
else
echo "❌ Error: Failed to retrieve content. Check TLS/SNI or URL expiration."
exit 1
fi
77 changes: 77 additions & 0 deletions skills/flutter-components/scripts/validate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import fs from 'node:fs';
import path from 'node:path';

async function validateFlutterComponent(filePath) {
const code = fs.readFileSync(filePath, 'utf-8');
const filename = path.basename(filePath);

console.log(`🔍 Scanning Flutter Component: ${filename}...`);

let issues = [];

// 1. Check for StatefulWidget requirement (from SKILL.md)
const isStateful = /extends\s+StatefulWidget/.test(code);

// 2. Check for "Props" (Constructor with parameters)
// In Flutter, we look for a constructor that isn't just the default one.
const hasConstructorWithParams = /[\w]+\(\{[\s\S]*?\}\)/.test(code);

// 3. Check for hardcoded hex colors
// Matches: 0xFFRRGGBB, 0xRRGGBB, #RRGGBB
const hexPattern = /0x([0-9A-Fa-f]{6,8})|#([0-9A-Fa-f]{6})/g;
let match;
let hexIssues = [];
while ((match = hexPattern.exec(code)) !== null) {
hexIssues.push(match[0]);
}

console.log(`--- Validation for: ${filename} ---`);

if (isStateful) {
console.log("✅ Component is a StatefulWidget.");
} else {
console.error("❌ MISSING: Must be a StatefulWidget (as per SKILL.md).");
issues.push("Not a StatefulWidget");
}

if (hasConstructorWithParams) {
console.log("✅ Constructor with parameters found (Props equivalent).");
} else {
console.error("❌ MISSING: Constructor with parameters (Props).");
issues.push("Missing constructor parameters");
}

if (hexIssues.length === 0) {
console.log("✅ No hardcoded hex values found.");
} else {
console.error(`❌ STYLE: Found ${hexIssues.length} hardcoded hex codes.`);
hexIssues.forEach(hex => console.error(` - ${hex}`));
issues.push("Hardcoded hex codes");
}

if (issues.length === 0) {
console.log("\n✨ FLUTTER COMPONENT VALID.");
process.exit(0);
} else {
console.error("\n🚫 VALIDATION FAILED.");
process.exit(1);
}
}

validateFlutterComponent(process.argv[2]);