Agent skill
flutter-development
Quellcode ansehen: travisjneuman/.claude
Installation
npx skills add travisjneuman/.claude --skill flutter-development156
Installationen
EU-hosted inference API
Power your AI agent skills with open-source models.
Drop-in OpenAI-compatible API. No data leaves Europe.
MiniMax
MiniMax M3
$0.40 / $1.40
per M tokens
Z.ai
GLM 5.3 Flash
$0.20 / $0.60
per M tokens
MoonshotAI
Kimi K3
$4.00 / $18.00
per M tokens
DeepSeek
DeepSeek V4 Pro
$1.80 / $3.60
per M tokens
Flutter Development
Build beautiful, natively compiled applications for mobile, web, desktop, and embedded from a single codebase.
Supported Platforms
| Platform | Status | Notes |
|---|---|---|
| iOS | Stable | Full native performance |
| Android | Stable | Full native performance |
| Web | Stable | PWA support |
| macOS | Stable | Native desktop |
| Windows | Stable | Native desktop |
| Linux | Stable | Native desktop |
Project Structure
my_app/
├── lib/
│ ├── main.dart
│ ├── app.dart
│ ├── features/
│ │ ├── home/
│ │ │ ├── home_screen.dart
│ │ │ ├── home_controller.dart
│ │ │ └── widgets/
│ │ └── settings/
│ ├── core/
│ │ ├── theme/
│ │ ├── utils/
│ │ └── constants/
│ └── shared/
│ ├── models/
│ ├── services/
│ └── widgets/
├── test/
├── pubspec.yaml
└── analysis_options.yamlBasic Structure
Main Entry
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const HomeScreen(),
);
}
}Stateless Widget
class GreetingCard extends StatelessWidget {
const GreetingCard({
super.key,
required this.name,
});
final String name;
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(
'Hello, $name!',
style: Theme.of(context).textTheme.headlineMedium,
),
),
);
}
}Stateful Widget
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0;
void _increment() {
setState(() {
_count++;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Count: $_count',
style: Theme.of(context).textTheme.headlineLarge,
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _increment,
child: const Text('Increment'),
),
],
);
}
}State Management
Riverpod (Recommended)
// Provider definition
final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) {
return CounterNotifier();
});
class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
void increment() => state++;
void decrement() => state--;
}
// Usage in widget
class CounterScreen extends ConsumerWidget {
const CounterScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Scaffold(
body: Center(
child: Text('Count: $count'),
),
floatingActionButton: FloatingActionButton(
onPressed: () => ref.read(counterProvider.notifier).increment(),
child: const Icon(Icons.add),
),
);
}
}
// Async provider
final itemsProvider = FutureProvider<List<Item>>((ref) async {
final repository = ref.watch(repositoryProvider);
return repository.fetchItems();
});BLoC Pattern
// Events
abstract class CounterEvent {}
class IncrementPressed extends CounterEvent {}
class DecrementPressed extends CounterEvent {}
// State
class CounterState {
final int count;
const CounterState(this.count);
}
// BLoC
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(const CounterState(0)) {
on<IncrementPressed>((event, emit) {
emit(CounterState(state.count + 1));
});
on<DecrementPressed>((event, emit) {
emit(CounterState(state.count - 1));
});
}
}
// Usage
BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text('Count: ${state.count}');
},
)Navigation
GoRouter (Recommended)
final router = GoRouter(
initialLocation: '/',
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomeScreen(),
routes: [
GoRoute(
path: 'details/:id',
builder: (context, state) {
final id = state.pathParameters['id']!;
return DetailsScreen(id: id);
},
),
],
),
GoRoute(
path: '/settings',
builder: (context, state) => const SettingsScreen(),
),
],
);
// Usage
MaterialApp.router(
routerConfig: router,
)
// Navigate
context.go('/details/123');
context.push('/settings');
context.pop();Common Widgets
Lists
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(item.imageUrl),
),
title: Text(item.title),
subtitle: Text(item.subtitle),
trailing: const Icon(Icons.chevron_right),
onTap: () => context.push('/details/${item.id}'),
);
},
)Forms
class LoginForm extends StatefulWidget {
const LoginForm({super.key});
@override
State<LoginForm> createState() => _LoginFormState();
}
class _LoginFormState extends State<LoginForm> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
void _submit() {
if (_formKey.currentState!.validate()) {
// Process login
}
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
prefixIcon: Icon(Icons.email),
),
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value == null || !value.contains('@')) {
return 'Enter a valid email';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: 'Password',
prefixIcon: Icon(Icons.lock),
),
obscureText: true,
validator: (value) {
if (value == null || value.length < 6) {
return 'Password must be at least 6 characters';
}
return null;
},
),
const SizedBox(height: 24),
ElevatedButton(
onPressed: _submit,
child: const Text('Login'),
),
],
),
);
}
}Networking
Dio HTTP Client
class ApiService {
final Dio _dio;
ApiService() : _dio = Dio(BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: const Duration(seconds: 10),
receiveTimeout: const Duration(seconds: 10),
)) {
_dio.interceptors.add(LogInterceptor());
}
Future<List<Item>> getItems() async {
try {
final response = await _dio.get('/items');
return (response.data as List)
.map((json) => Item.fromJson(json))
.toList();
} on DioException catch (e) {
throw ApiException(e.message ?? 'Unknown error');
}
}
}Local Storage
Hive (Recommended)
// Model
@HiveType(typeId: 0)
class Item extends HiveObject {
@HiveField(0)
late String id;
@HiveField(1)
late String name;
}
// Setup
await Hive.initFlutter();
Hive.registerAdapter(ItemAdapter());
await Hive.openBox<Item>('items');
// Usage
final box = Hive.box<Item>('items');
await box.put('key', item);
final item = box.get('key');SharedPreferences
final prefs = await SharedPreferences.getInstance();
await prefs.setString('token', 'abc123');
final token = prefs.getString('token');Platform-Specific Code
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart' show kIsWeb;
Widget build(BuildContext context) {
if (kIsWeb) {
return WebLayout();
} else if (Platform.isIOS) {
return CupertinoLayout();
} else if (Platform.isAndroid) {
return MaterialLayout();
} else if (Platform.isMacOS || Platform.isWindows || Platform.isLinux) {
return DesktopLayout();
}
return DefaultLayout();
}Testing
Widget Tests
testWidgets('Counter increments', (tester) async {
await tester.pumpWidget(const MaterialApp(home: Counter()));
expect(find.text('Count: 0'), findsOneWidget);
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
expect(find.text('Count: 1'), findsOneWidget);
});Unit Tests
test('Item fromJson parses correctly', () {
final json = {'id': '1', 'name': 'Test'};
final item = Item.fromJson(json);
expect(item.id, '1');
expect(item.name, 'Test');
});Performance
Best Practices
// Use const constructors
const SizedBox(height: 16)
// Avoid rebuilds with const
const MyStaticWidget()
// Use ListView.builder for long lists
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) => ItemWidget(items[index]),
)
// Cache images
CachedNetworkImage(
imageUrl: url,
placeholder: (context, url) => const CircularProgressIndicator(),
)Best Practices
DO:
- Use const constructors everywhere possible
- Split widgets into smaller components
- Use Riverpod or BLoC for state
- Follow Flutter naming conventions
- Write widget tests
DON'T:
- Put logic in build methods
- Create god widgets
- Use setState for complex state
- Ignore null safety
- Skip code generation setup
Dart 3 Features
Sealed Classes and Pattern Matching
// Sealed classes for exhaustive pattern matching
sealed class Result<T> {}
class Success<T> extends Result<T> { final T value; Success(this.value); }
class Failure<T> extends Result<T> { final String error; Failure(this.error); }
// Exhaustive switch
String display(Result<User> result) => switch (result) {
Success(value: final user) => 'Hello, ${user.name}',
Failure(error: final msg) => 'Error: $msg',
};Records
// Lightweight data tuples
(String, int) getUserInfo() => ('John', 30);
// Named fields
({String name, int age}) getUserDetails() => (name: 'John', age: 30);
// Destructuring
final (name, age) = getUserInfo();
final (:name, :age) = getUserDetails();Extension Types
// Zero-cost type wrappers (compile-time only)
extension type UserId(String value) {
bool get isValid => value.isNotEmpty;
}
extension type Email(String value) {
factory Email.validated(String raw) {
if (!raw.contains('@')) throw FormatException('Invalid email');
return Email(raw);
}
}
// Usage - type-safe, zero runtime cost
void sendEmail(Email to, UserId from) { ... }
sendEmail(Email.validated('a@b.com'), UserId('user123'));If-Case and Switch Expressions
// If-case for pattern matching
if (json case {'name': String name, 'age': int age}) {
print('$name is $age years old');
}
// Switch expressions (concise)
final icon = switch (status) {
'active' => Icons.check_circle,
'pending' => Icons.hourglass_empty,
'error' => Icons.error,
_ => Icons.help,
};Riverpod 3.0 Patterns
// Modern Riverpod with code generation
@riverpod
class Counter extends _$Counter {
@override
int build() => 0;
void increment() => state++;
void decrement() => state--;
}
// Async provider with Riverpod 3.0
@riverpod
Future<List<Item>> items(Ref ref) async {
final repository = ref.watch(repositoryProvider);
return repository.fetchItems();
}
// Family provider (parameterized)
@riverpod
Future<User> user(Ref ref, String userId) async {
return ref.watch(apiClientProvider).getUser(userId);
}
// Usage in widget
class ItemScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final items = ref.watch(itemsProvider);
return items.when(
data: (data) => ListView.builder(
itemCount: data.length,
itemBuilder: (context, i) => ItemTile(data[i]),
),
loading: () => const CircularProgressIndicator(),
error: (err, stack) => Text('Error: $err'),
);
}
}Material 3 / Material You
MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.light,
),
useMaterial3: true,
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.dark,
),
useMaterial3: true,
),
themeMode: ThemeMode.system,
);
// Dynamic color (Android 12+)
import 'package:dynamic_color/dynamic_color.dart';
DynamicColorBuilder(
builder: (lightDynamic, darkDynamic) {
return MaterialApp(
theme: ThemeData(
colorScheme: lightDynamic ?? defaultLightScheme,
useMaterial3: true,
),
);
},
);Build and Deployment
Fastlane
# fastlane/Fastfile
platform :ios do
lane :beta do
build_flutter_app
upload_to_testflight
end
end
platform :android do
lane :beta do
build_flutter_app
upload_to_play_store(track: 'internal')
end
endCodemagic CI/CD
# codemagic.yaml
workflows:
flutter-release:
name: Flutter Release
environment:
flutter: stable
scripts:
- name: Build
script: flutter build appbundle --release
artifacts:
- build/**/outputs/**/*.aab
publishing:
google_play:
credentials: $GCLOUD_SERVICE_ACCOUNT
track: internalEAS Build (for Expo/RN comparison reference)
Flutter apps typically use Fastlane, Codemagic, or GitHub Actions for CI/CD rather than EAS.
Installationen
Sicherheitsprüfung
Quellcode ansehen
travisjneuman/.claude
Mehr aus dieser Quelle
Give agents clean web context
Search and extract the public web as Markdown or structured JSON through one API or hosted MCP server.
Verwandte Skills
Mehr aus dieser Quelle: travisjneuman/.claude
So verwenden Sie diesen Skill
Install flutter-development by running npx skills add travisjneuman/.claude --skill flutter-development in your project directory. Führen Sie den obigen Installationsbefehl in Ihrem Projektverzeichnis aus. Die Skill-Datei wird von GitHub heruntergeladen und in Ihrem Projekt platziert.
Keine Konfiguration erforderlich. Ihr KI-Agent (Claude Code, Cursor, Windsurf usw.) erkennt installierte Skills automatisch und nutzt sie als Kontext bei der Code-Generierung.
Der Skill verbessert das Verständnis Ihres Agenten für flutter-development, und hilft ihm, etablierte Muster zu befolgen, häufige Fehler zu vermeiden und produktionsreifen Code zu erzeugen.
Was Sie erhalten
Skills sind Klartext-Anweisungsdateien — kein ausführbarer Code. Sie kodieren Expertenwissen über Frameworks, Sprachen oder Tools, das Ihr KI-Agent liest, um seine Ausgabe zu verbessern. Das bedeutet null Laufzeit-Overhead, keine Abhängigkeitskonflikte und volle Transparenz: Sie können jede Anweisung vor der Installation lesen und prüfen.
Kompatibilität
Dieser Skill funktioniert mit jedem KI-Coding-Agenten, der das skills.sh-Format unterstützt, einschließlich Claude Code (Anthropic), Cursor, Windsurf, Cline, Aider und anderen Tools, die projektbezogene Kontextdateien lesen. Skills sind auf Transportebene framework-agnostisch — der Inhalt bestimmt, für welche Sprache oder welches Framework er gilt.