원클릭으로
flutter-architecture
Use the Flutter team's recommended app architecture
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Use the Flutter team's recommended app architecture
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Configure your Flutter app to support assistive technologies like Screen Readers
Add animated effects to your Flutter app
Measure and reduce the size of the Flutter app bundle, APK, or IPA
Execute long-running tasks in a background thread in Flutter
Set up a macOS environment for Flutter development
Adding a Home Screen widget to your Flutter App
| name | flutter-architecture |
| description | Use the Flutter team's recommended app architecture |
| metadata | {"model":"models/gemini-3.1-pro-preview","last_modified":"Thu, 26 Feb 2026 23:42:30 GMT"} |
Implements a scalable, maintainable Flutter application architecture using the MVVM pattern, unidirectional data flow, and strict separation of concerns across UI, Domain, and Data layers. Assumes a standard Flutter environment utilizing provider for dependency injection and ListenableBuilder for reactive UI updates.
Before implementing a feature, evaluate the architectural requirements using the following logic:
Analyze Feature Requirements Evaluate the requested feature to determine the necessary data models, services, and UI state. STOP AND ASK THE USER: "Please provide the specific data models, API endpoints, or local storage requirements for this feature, and confirm if complex business logic requires a dedicated Domain (UseCase) layer."
Implement the Data Layer: Services Create a stateless service class to wrap the external API or local storage. This class must not contain business logic or state.
class SharedPreferencesService {
static const String _kDarkMode = 'darkMode';
Future<void> setDarkMode(bool value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_kDarkMode, value);
}
Future<bool> isDarkMode() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(_kDarkMode) ?? false;
}
}
Implement the Data Layer: Repositories
Create a repository to act as the single source of truth. The repository consumes the service, handles errors using Result objects, and exposes domain models or streams.
class ThemeRepository {
ThemeRepository(this._service);
final _darkModeController = StreamController<bool>.broadcast();
final SharedPreferencesService _service;
Future<Result<bool>> isDarkMode() async {
try {
final value = await _service.isDarkMode();
return Result.ok(value);
} on Exception catch (e) {
return Result.error(e);
}
}
Future<Result<void>> setDarkMode(bool value) async {
try {
await _service.setDarkMode(value);
_darkModeController.add(value);
return Result.ok(null);
} on Exception catch (e) {
return Result.error(e);
}
}
Stream<bool> observeDarkMode() => _darkModeController.stream;
}
Implement the UI Layer: ViewModels
Create a ChangeNotifier to manage UI state. Use the Command pattern to handle user interactions and asynchronous repository calls.
class ThemeSwitchViewModel extends ChangeNotifier {
ThemeSwitchViewModel(this._themeRepository) {
load = Command0(_load)..execute();
toggle = Command0(_toggle);
}
final ThemeRepository _themeRepository;
bool _isDarkMode = false;
bool get isDarkMode => _isDarkMode;
late final Command0<void> load;
late final Command0<void> toggle;
Future<Result<void>> _load() async {
final result = await _themeRepository.isDarkMode();
if (result is Ok<bool>) {
_isDarkMode = result.value;
}
notifyListeners();
return result;
}
Future<Result<void>> _toggle() async {
_isDarkMode = !_isDarkMode;
final result = await _themeRepository.setDarkMode(_isDarkMode);
notifyListeners();
return result;
}
}
Implement the UI Layer: Views
Create a StatelessWidget that observes the ViewModel using ListenableBuilder. The View must contain zero business logic.
class ThemeSwitch extends StatelessWidget {
const ThemeSwitch({super.key, required this.viewmodel});
final ThemeSwitchViewModel viewmodel;
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
children: [
const Text('Dark Mode'),
ListenableBuilder(
listenable: viewmodel,
builder: (context, _) {
return Switch(
value: viewmodel.isDarkMode,
onChanged: (_) {
viewmodel.toggle.execute();
},
);
},
),
],
),
);
}
}
Wire Dependencies
Inject the dependencies at the application or route level using constructor injection or a dependency injection framework like provider.
void main() {
runApp(
MainApp(
themeRepository: ThemeRepository(SharedPreferencesService()),
),
);
}
Validate and Fix Review the generated implementation against the constraints. Ensure that data flows strictly downwards (Repository -> ViewModel -> View) and events flow strictly upwards (View -> ViewModel -> Repository). If a View contains data mutation logic, extract it to the ViewModel. If a ViewModel directly accesses an API, extract it to a Service and route it through a Repository.
Result (Ok/Error) objects to the ViewModels.