new-feature-scaffold
Provides a checklist and directory structure templates to standardize feature creation in a Flutter application.
Install
mkdir -p .claude/skills/new-feature-scaffold && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/17203" && unzip -o skill.zip -d .claude/skills/new-feature-scaffold && rm skill.zipInstalls to .claude/skills/new-feature-scaffold
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Step-by-step checklist and templates for adding a new feature to this Flutter social media app — directory structure, model, service, cubit, view, and routing wiring.Key capabilities
- →Create directory structure for a new feature
- →Generate read models (`{name}_model.dart`)
- →Generate write models (`{name}_request_body.dart`)
- →Create service classes (`{name}_services.dart`)
- →Scaffold Cubit state and logic (`{name}_state.dart`, `{name}_cubit.dart`)
- →Wire routing for the new feature
How it works
The skill creates a predefined directory structure, generates template code for models, services, Cubits, and views, and configures routing based on a provided feature name, following a 4-layer architecture.
Inputs & outputs
When to use new-feature-scaffold
- →Creating new screen features
- →Adding new data entities
- →Extending feature functionality
- →Standardizing feature implementation
About this skill
New Feature Scaffold — Social Media App
Complete guide to add a new feature end-to-end, following the project's strict 4-layer architecture.
When to Use
- Building a new screen or feature (notifications, DMs, explore, etc.)
- Adding a new entity to the data model
- Extending an existing feature with a new sub-page
Step 0: Locate Reference Feature
Before writing anything, read an existing, complete feature:
# Profile is a good reference — has model, service, cubit, views, and routing
find lib/features/profile -type f -name "*.dart" | sort
Read each file to anchor your implementation to project conventions.
Step 1: Create Directory Structure
mkdir -p lib/features/{name}/models
mkdir -p lib/features/{name}/services
mkdir -p lib/features/{name}/cubit
mkdir -p lib/features/{name}/views/pages
mkdir -p lib/features/{name}/views/widgets
Step 2: Models
Read Model ({name}_model.dart)
class {Name}Model {
final String id;
final String authorId;
// add fields...
// Optional enrichment fields (null until populated by cubit)
final String? authorName;
final String? authorImageUrl;
const {Name}Model({
required this.id,
required this.authorId,
this.authorName,
this.authorImageUrl,
});
factory {Name}Model.fromMap(Map<String, dynamic> map) {
return {Name}Model(
id: map['id'] as String? ?? '',
authorId: map['author_id'] as String? ?? '',
);
}
Map<String, dynamic> toMap() {
return {
'id': id,
'author_id': authorId,
};
}
{Name}Model copyWith({
String? id,
String? authorId,
String? authorName,
String? authorImageUrl,
}) {
return {Name}Model(
id: id ?? this.id,
authorId: authorId ?? this.authorId,
authorName: authorName ?? this.authorName,
authorImageUrl: authorImageUrl ?? this.authorImageUrl,
);
}
factory {Name}Model.fromJson(Map<String, dynamic> json) =>
{Name}Model.fromMap(json);
Map<String, dynamic> toJson() => toMap();
}
Write Model ({name}_request_body.dart)
class {Name}RequestBody {
final String authorId;
final String content;
const {Name}RequestBody({
required this.authorId,
required this.content,
});
Map<String, dynamic> toMap() {
return {
'author_id': authorId,
'content': content,
};
}
}
Step 3: Service ({name}_services.dart)
import 'package:social_media_app/core/services/supabase_database_services.dart';
import 'package:social_media_app/core/utils/app_tables_names.dart';
import '../models/{name}_model.dart';
import '../models/{name}_request_body.dart';
class {Name}Services {
final _db = SupabaseDatabaseServices();
Future<List<{Name}Model>> fetch{Name}s() async {
return await _db.fetchRows<{Name}Model>(
tableName: AppTablesNames.{name}s,
fromMap: {Name}Model.fromMap,
);
}
Future<void> create{Name}({Name}RequestBody body) async {
await _db.insertRow(
tableName: AppTablesNames.{name}s,
data: body.toMap(),
);
}
Future<void> delete{Name}(String id) async {
await _db.deleteRow(
tableName: AppTablesNames.{name}s,
column: 'id',
value: id,
);
}
}
Add the table constant to
AppTablesNamesif it doesn't exist yet.
Step 4: Cubit
{name}_state.dart
part of '{name}_cubit.dart';
abstract class {Name}State {}
class {Name}Initial extends {Name}State {}
class {Name}Loading extends {Name}State {}
class {Name}Loaded extends {Name}State {
final List<{Name}Model> items;
{Name}Loaded({required this.items});
}
class {Name}Success extends {Name}State {}
class {Name}Error extends {Name}State {
final String message;
{Name}Error({required this.message});
}
{name}_cubit.dart
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:social_media_app/core/services/core_auth_services.dart';
import '../models/{name}_model.dart';
import '../models/{name}_request_body.dart';
import '../services/{name}_services.dart';
part '{name}_state.dart';
class {Name}Cubit extends Cubit<{Name}State> {
{Name}Cubit() : super({Name}Initial());
final _{Name}Services _services = {Name}Services();
final _coreAuthServices = CoreAuthServices();
Future<void> fetch{Name}s() async {
emit({Name}Loading());
try {
final raw = await _services.fetch{Name}s();
final enriched = <{Name}Model>[];
for (var item in raw) {
final userData = await _coreAuthServices.getUserData(item.authorId);
if (userData != null) {
item = item.copyWith(
authorName: userData.name,
authorImageUrl: userData.imageUrl,
);
}
enriched.add(item);
}
emit({Name}Loaded(items: enriched));
} catch (e) {
emit({Name}Error(message: e.toString()));
}
}
Future<void> create{Name}({Name}RequestBody body) async {
try {
await _services.create{Name}(body);
emit({Name}Success());
await fetch{Name}s();
} catch (e) {
emit({Name}Error(message: e.toString()));
}
}
}
Step 5: Page ({name}_page.dart)
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:social_media_app/core/utils/theme/app_colors.dart';
import '../cubit/{name}_cubit.dart';
class {Name}Page extends StatelessWidget {
const {Name}Page({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => {Name}Cubit()..fetch{Name}s(),
child: const _{Name}View(),
);
}
}
class _{Name}View extends StatelessWidget {
const _{Name}View();
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.background,
appBar: AppBar(title: const Text('{Name}')),
body: BlocBuilder<{Name}Cubit, {Name}State>(
builder: (context, state) {
if (state is {Name}Loading) {
return const Center(child: CircularProgressIndicator());
}
if (state is {Name}Loaded) {
return _{Name}ListView(items: state.items);
}
if (state is {Name}Error) {
return Center(child: Text(state.message));
}
return const SizedBox.shrink();
},
),
);
}
}
Step 6: Wire Routing
app_routes.dart — add the constant
static const String {name}Route = '/{name}';
app_router.dart — add the case
case AppRoutes.{name}Route:
return CupertinoPageRoute(
builder: (_) => const {Name}Page(),
settings: settings,
);
If the route needs arguments:
- Create
{Name}PageArgsinfeatures/{name}/models/ - Cast in
app_router.dart:final args = settings.arguments as {Name}PageArgs; - Pass to page constructor
Step 7: Cubit Scope Decision
| Scenario | Action |
|---|---|
| Feature cubit only needed on one page | Provide inside the page (Step 5 pattern) |
| Cubit must survive tab switches | Provide in main.dart at app root |
| Cubit is an existing instance from another route | Use BlocProvider.value in app_router.dart |
Step 8: Final Checklist
-
{Name}Model:fromMap,toMap,copyWith,fromJson,toJson -
{Name}RequestBody:toMaponly - Service only uses
SupabaseDatabaseServicesandAppTablesNames - State file is
part ofthe cubit file - All states handled in BlocBuilder (no fallthrough)
- Only
AppColors.*used — noColors.*or hex literals - Route constant added to
AppRoutes - Case added to
AppRouter.generateRouteusingCupertinoPageRoute - Typed
*PageArgsmodel used if route takes multiple params - Cubit provided at correct scope
-
flutter analyzepasses with no issues
When not to use it
- →When not building a new screen or feature
- →When not adding a new entity to the data model
- →When not extending an existing feature with a new sub-page
Limitations
- →Specific to Flutter social media app architecture
- →Assumes a 4-layer architecture
- →Requires manual addition of table constants to `AppTablesNames`
How it compares
This skill automates the creation of a new feature's boilerplate code and structure according to strict project conventions, ensuring consistency and adherence to the 4-layer architecture, unlike manual setup.
Compared to similar skills
new-feature-scaffold side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| new-feature-scaffold (this skill) | 0 | 3mo | Review | Beginner |
| flutter-development | 1,555 | 5mo | No flags | Intermediate |
| flutter-expert | 73 | 4mo | No flags | Advanced |
| flutter | 13 | 4mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
flutter-development
aj-geddes
Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.
flutter-expert
sickn33
Master Flutter development with Dart 3, advanced widgets, and multi-platform deployment. Handles state management, animations, testing, and performance optimization for mobile, web, desktop, and embedded platforms. Use PROACTIVELY for Flutter architecture, UI implementation, or cross-platform features.
flutter
alinaqi
Flutter development with Riverpod state management, Freezed, go_router, and mocktail testing
mobile-developer
sickn33
Develop React Native, Flutter, or native mobile apps with modern architecture patterns. Masters cross-platform development, native integrations, offline sync, and app store optimization. Use PROACTIVELY for mobile features, cross-platform code, or app optimization.
stac-quickstart
StacDev
Help initialize and validate a Stac-enabled Flutter project and ship a first server-driven screen. Use when users ask to set up Stac CLI, run stac init/build/deploy, verify project prerequisites, or troubleshoot first-run setup and missing configuration files.
flutter-init
bear2u
Use when user wants to create a new Flutter project (Todo/Habit/Note/Expense/Custom domain) with Clean Architecture, Riverpod 3.0, Drift, and modern Flutter stack