Getting Started
Installation
Dart pub global로 설치합니다. Flutter SDK가 설치되어 있어야 합니다.
$ dart pub global activate flutist
flutist 명령어를 사용하려면 ~/.pub-cache/bin이 PATH에 등록되어 있어야 합니다.
Quick Start
- 초기화TERMINAL
$ mkdir my_app && cd my_app $ flutist inity입력 시 Flutter 프로젝트를 생성하고 Flutist 설정 파일을 추가합니다. - 패키지 등록TERMINAL
$ flutist pub add http bloc flutter_bloc버전을 자동으로 해결하여
package.dart에 추가합니다. - 모듈 생성TERMINAL
$ flutist create --name auth --path features --options cleanauth_domain,auth_data,auth_presentation3개 레이어를 자동 생성하고project.dart에 의존성을 연결합니다. - 전체 동기화TERMINAL
$ flutist generate모든
pubspec.yaml이 즉시 동기화됩니다. 아키텍처 위반 시 이 단계에서 중단됩니다.
프로젝트 구조
my_app/
app/ # 앱 셸 (simple 타입)
lib/app.dart
pubspec.yaml
features/
auth/
auth_domain/ # UseCase, Entity, Repository interface
lib/auth_domain.dart
pubspec.yaml
auth_data/ # Repository 구현체, API 클라이언트
lib/auth_data.dart
pubspec.yaml
auth_presentation/ # UI, BLoC
lib/auth_presentation.dart
pubspec.yaml
flutist/
flutist_gen.dart # ⚠ 자동 생성 — 수정 금지
templates/
lib/main.dart
package.dart # 의존성 버전 레지스트리
project.dart # 모듈 선언
pubspec.yaml # 루트 workspaceWhy Flutist?
모듈화는 대규모 Flutter 프로젝트의 표준 접근 방식입니다. 독립 빌드, 병렬 개발, 테스트 격리 — 효과는 분명합니다. 그러나 모듈이 늘어날수록 관리 비용도 함께 커집니다. Flutist는 이 관리 비용을 자동화로 제거합니다.
타입 안전한 의존성 관리
모듈이 10개 넘어가면 패키지 버전 불일치가 발생하기 쉽습니다. package.dart에 버전을 한 번 선언하면 flutist generate가 flutist_gen.dart를 자동 생성합니다. 이후 모든 모듈에서 IDE 자동완성과 타입 검사를 통해 안전하게 참조할 수 있습니다.
// 1. package.dart — 버전을 한 번만 선언
Dependency(name: 'dio', version: '^5.3.0'),
Dependency(name: 'flutter_bloc', version: '^8.1.6'),
// 2. flutist_gen.dart — flutist generate가 자동 생성
Dependency get dio => dependencies.firstWhere((d) => d.name == 'dio');
Dependency get flutterBloc => dependencies.firstWhere((d) => d.name == 'flutter_bloc');
Module get authDomain => modules.firstWhere((m) => m.name == 'auth_domain');
// 3. project.dart — 타입 안전하게 참조 (IDE 자동완성 ✅)
Module(
name: 'auth_data',
dependencies: [package.dependencies.dio, package.dependencies.flutterBloc],
modules: [package.modules.authDomain],
),pubspec.yaml 중앙 관리
모듈 수만큼 늘어나는 pubspec.yaml. 하나의 패키지 버전을 올리려면 이를 참조하는 모든 파일을 직접 수정해야 합니다.
Flutist는 project.dart 선언을 기반으로 모든 pubspec.yaml을 자동 동기화합니다. 개발자가 직접 수정하는 건 project.dart 단 한 파일입니다.
$ flutist generate
✓ pubspec.yaml 동기화: app, auth_domain, auth_data, auth_presentation,
product_interface, product_implementation ... (총 24개)
✓ 모든 아키텍처 규칙 통과
✓ 완료 (0.8s)아키텍처 규칙 자동화
문서나 코드 리뷰만으로는 아키텍처 규칙을 일관되게 유지하기 어렵습니다. 바쁜 개발 과정에서 domain이 http를 import하거나, 다른 피처의 구현체를 직접 참조하는 일이 생깁니다. 이런 위반은 발견하기 어렵고, 발견했을 때는 이미 여러 곳에 퍼진 경우가 많습니다.
Flutist는 아키텍처 규칙을 실행 가능한 코드로 만듭니다. strictMode: true(기본값)에서 위반이 감지되면 generate가 즉시 중단됩니다. 문서에만 존재하던 원칙이 빌드 게이트가 됩니다.
$ flutist generate
✗ [B4] auth_domain → auth_data: 역방향 의존성 감지 — domain은 data를 알 수 없음
→ auth_domain에서 Dio import를 제거하고 Repository interface만 선언하세요
✗ generate 중단 (strictMode: true)새로 합류한 팀원이 아키텍처를 완전히 이해하지 못해도, 규칙을 어기는 순간 명확한 피드백을 받습니다. 코드 리뷰에서 아키텍처 위반을 지적하는 대신, 도구가 자동으로 검사합니다.
보일러플레이트 자동 생성
Modular Architecture의 가장 큰 불편함 중 하나는 보일러플레이트입니다. 피처 하나를 추가할 때마다 interface, implementation, testing, tests, example 패키지를 만들고, 각각의 pubspec.yaml, lib/ 구조, 배럴 파일까지 세팅해야 합니다.
BLoC처럼 파일이 많은 상태관리 패턴은 피처 하나에 이벤트, 상태, BLoC, 페이지, 위젯 파일이 모두 필요합니다. flutist create와 flutist scaffold로 이 모든 것을 명령어 한 번에 생성합니다.
# micro 타입으로 todos 피처 생성 — 5개 패키지 + 구조 전체 자동화
$ flutist create --name todos --path features --options micro
✓ features/todos/todos_interface 생성
✓ features/todos/todos_implementation 생성
✓ features/todos/todos_testing 생성
✓ features/todos/todos_tests 생성
✓ features/todos/todos_example 생성
# BLoC scaffold로 모든 파일 생성
$ flutist scaffold --template bloc --name todos_overview --path features/todos/todos_implementation
✓ todos_overview_bloc.dart 생성
✓ todos_overview_event.dart 생성
✓ todos_overview_state.dart 생성package.dart
프로젝트에서 사용하는 모든 외부 패키지 버전과 모듈 이름을 한 곳에서 선언합니다. 버전은 이 파일에서만 관리됩니다.
import 'package:flutist/flutist.dart';
final package = Package(
name: 'my_app',
dependencies: [
Dependency(name: 'http', version: '^1.1.0'),
Dependency(name: 'bloc', version: '^8.1.0'),
Dependency(name: 'flutter_bloc', version: '^8.1.0'),
Dependency(name: 'test', version: '^1.24.0'),
Dependency(name: 'mockito', version: '^5.4.4'),
],
modules: [
Module(name: 'app'),
Module(name: 'auth_domain'),
Module(name: 'auth_data'),
Module(name: 'auth_presentation'),
],
);파싱은 Regex 기반입니다. Module(name: 'foo')를 한 줄에 쓰면 인식되지 않습니다. 반드시 멀티라인 형식을 사용하세요.
project.dart
각 모듈의 의존성과 모듈 간 관계를 선언합니다. flutist generate가 이 파일을 읽어 모든 pubspec.yaml을 동기화합니다.
import 'package:flutist/flutist.dart';
import 'flutist/flutist_gen.dart';
import 'package.dart';
final project = Project(
name: 'my_app',
options: const ProjectOptions(
strictMode: true, // 위반 시 generate 중단
compositionRoots: ['app'], // _implementation 직접 참조 허용 모듈
),
modules: [
Module(
name: 'auth_domain',
dependencies: [],
devDependencies: [package.dependencies.test],
modules: [],
),
Module(
name: 'auth_data',
dependencies: [package.dependencies.http],
devDependencies: [package.dependencies.test, package.dependencies.mockito],
modules: [package.modules.authDomain],
),
Module(
name: 'auth_presentation',
dependencies: [package.dependencies.bloc, package.dependencies.flutterBloc],
devDependencies: [package.dependencies.test],
modules: [package.modules.authDomain],
),
Module(
name: 'app',
modules: [package.modules.authPresentation],
),
],
);Module 필드
| 필드 | 타입 | 설명 |
|---|---|---|
name | String | 모듈 이름. package.dart의 modules 목록에 있어야 함 |
dependencies | List<Dependency> | 외부 패키지 의존성 (package.dependencies.xxx) |
devDependencies | List<Dependency> | 개발용 의존성 (test, mockito 등) |
modules | List<Module> | 의존하는 다른 모듈 (package.modules.xxx) |
flutist_gen.dart
flutist generate 실행 시 자동 생성됩니다. package.dependencies.http, package.modules.authDomain처럼 IDE 자동완성이 동작하는 타입 세이프 접근자를 제공합니다. 직접 수정하지 마세요.
// ⚠️ Auto-generated by flutist. Do not modify.
extension PackageDependenciesX on List<Dependency> {
Dependency get http => firstWhere((d) => d.name == 'http');
Dependency get bloc => firstWhere((d) => d.name == 'bloc');
Dependency get flutterBloc => firstWhere((d) => d.name == 'flutter_bloc');
Dependency get test => firstWhere((d) => d.name == 'test');
}
extension PackageModulesX on List<Module> {
Module get app => firstWhere((m) => m.name == 'app');
Module get authDomain => firstWhere((m) => m.name == 'auth_domain');
Module get authData => firstWhere((m) => m.name == 'auth_data');
Module get authPresentation => firstWhere((m) => m.name == 'auth_presentation');
}Module Type: clean
Clean Architecture 기반 3-layer 피처 모듈. UI, 비즈니스 로직, 데이터를 엄격하게 분리합니다.
presentationdatadomain$ flutist create --name auth --path features --options clean자동 연결되는 project.dart 항목
Module(name: 'auth_domain', modules: []),
Module(
name: 'auth_data',
modules: [package.modules.authDomain], # data → domain
),
Module(
name: 'auth_presentation',
modules: [package.modules.authDomain], # presentation → domain (data 아님)
),Module Type: micro
Microfeature Architecture 기반 5-layer. 외부에서 재사용되는 라이브러리 모듈에 적합합니다. 소비자는 오직 interface에만 의존합니다.
exampleteststestingimplementationinterface$ flutist create --name network --path packages --options microModule Type: lite
micro에서 example을 제거한 4-layer. 데모 앱이 필요 없는 내부 API 모듈에 적합합니다.
teststestingimplementationinterface$ flutist create --name storage --path packages --options liteModule Type: simple
단일 패키지. 레이어 분리 없이 유틸리티, 공유 모델, 앱 셸에 사용합니다.
$ flutist create --name utils --path packagespackages/utils/
lib/utils.dart
pubspec.yaml
analysis_options.yaml
README.mdflutist init
Flutist 프로젝트를 초기화합니다. 신규 프로젝트와 기존 프로젝트 마이그레이션 두 흐름을 지원합니다.
신규 프로젝트 (pubspec.yaml 없음)
$ mkdir my_app && cd my_app
$ flutist init
Do you want to create a new Flutter project? (y/n): y
▶ flutter create . --project-name my_app
▶ project.dart / package.dart 생성
▶ app/ 모듈 생성 (simple 타입)
▶ flutist_gen.dart 생성기존 프로젝트 마이그레이션
$ cd existing_project && flutist init
1) New project 2) Migration?: 2
▶ project.dart / package.dart 생성 (기존 pubspec.yaml 보존)
▶ flutist_gen.dart 생성flutist create
새 모듈을 생성합니다. 디렉토리, pubspec.yaml, barrel 파일을 자동으로 만들고 project.dart, package.dart에 자동 등록합니다.
| 옵션 | 단축키 | 필수 | 설명 |
|---|---|---|---|
--name | -n | ✓ | 모듈 이름 (snake_case) |
--path | -p | ✓ | 생성할 디렉토리 경로 |
--options | -o | clean · micro · lite (미지정 시 simple) |
$ flutist create --name auth --path features --options clean
$ flutist create --name network --path packages --options micro
$ flutist create --name storage --path packages --options lite
$ flutist create --name utils --path packages모듈 이름에 _domain, _implementation 등 레이어 suffix를 직접 붙이지 마세요. Flutist가 자동으로 처리합니다.
flutist generate
project.dart를 읽어 모든 모듈의 pubspec.yaml을 동기화합니다. 아키텍처 규칙 검사도 함께 실행됩니다.
- package.dart 파싱
의존성 목록과 모듈 이름 목록을 읽습니다.
- project.dart 파싱
모듈 설정, strictMode, compositionRoots를 읽습니다.
- 아키텍처 규칙 검사
5가지 규칙 실행. strictMode: true이면 위반 시 exit 1.
- flutist_gen.dart 재생성
타입 세이프 접근자 코드를 갱신합니다.
- pubspec.yaml 전체 동기화
SDK 의존성과 주석을 보존하며 dependencies/path 참조를 업데이트합니다.
flutist check
파일 생성 없이 아키텍처 규칙만 검사합니다. CI 파이프라인에 추가하기 적합합니다.
$ flutist check
✓ [B1] Implementation references: OK
✓ [B2] Testing layer references: OK
✓ [B3] Example layer references: OK
✗ [B4] auth_domain → auth_data: domain must not depend on data
✓ [B5] Circular dependencies: OKflutist pub
외부 패키지를 package.dart에 추가합니다. dart pub add로 버전을 자동 해결합니다.
$ flutist pub add http
$ flutist pub add bloc flutter_bloc freezed추가 후 package.dart에 Dependency 항목이 삽입되고 flutist_gen.dart가 재생성됩니다. 이후 package.dependencies.http로 접근할 수 있습니다.
flutist scaffold
템플릿으로 저장하고 재사용합니다. 자세한 내용은 아래 Scaffold 섹션을 참고하세요.
$ flutist scaffold list
$ flutist scaffold bloc_feature --name login
$ flutist scaffold bloc_feature --name login --path lib/featuresflutist test
모든 모듈의 테스트를 병렬로 실행합니다. Flutter 의존성 유무를 자동으로 감지해 flutter test 또는 dart test를 선택합니다.
$ flutist test
$ flutist test --module auth_domainflutist graph
모듈 간 의존성 그래프를 시각화합니다.
| 옵션 | 기본값 | 설명 |
|---|---|---|
--format | mermaid | mermaid · dot · ascii |
--output | - | 출력 파일 경로 |
--open | false | 브라우저에서 바로 열기 (mermaid) |
$ flutist graph --open
$ flutist graph --format dot --output graph.dot
$ flutist graph --format asciiArchitecture
Flutist는 아키텍처 규칙을 flutist generate·flutist check 단계에서 자동으로 검사합니다. 문서에만 존재하던 원칙이 실행 가능한 코드가 됩니다.
Microfeature Architecture
Microfeature Architecture는 하나의 기능(피처)을 역할이 명확한 5개 레이어로 쪼개 각각 독립 패키지로 만드는 설계 패턴입니다. iOS 생태계의 Tuist, Point-Free의 모듈화 가이드라인에서 발전했으며, 대규모 Flutter 프로젝트에서 완전한 모듈 격리가 필요할 때 사용됩니다.
핵심 아이디어는 하나입니다 — 소비자는 interface만 알고, 구현체는 composition root가 주입한다. 이 원칙 하나로 의존성 방향이 일관되고, 테스트가 격리되고, 피처 간 결합이 제거됩니다.
레이어 구조
장점
- 소비자 격리 — 소비자 모듈은 interface 패키지만 알면 됩니다. 구현체가 교체되어도 영향 없음.
- 테스트 속도 — Fake로 외부 의존성 없이 BLoC·Repository를 즉시 테스트.
- 피처 독립 실행 — _example 앱으로 피처를 앱 없이 단독 실행·확인.
- 점진적 교체 — implementation만 교체하면 나머지 레이어는 그대로 유지.
Implementation 참조 제한
implementation은 내부 구현 세부사항입니다. 소비자 모듈이 구현체를 직접 알게 되면 Microfeature의 격리 원칙이 무너집니다. 구현체는 오직 composition root(보통 app)가 의존성을 주입하는 방식으로만 사용됩니다.
example·tests를 제외한 모든 모듈에서 implementation 직접 참조는 아키텍처 위반입니다.허용
app(composition root) →network_implementation— 구현체 주입 지점network_example→network_implementation— 같은 피처 데모 앱network_tests→network_implementation— 같은 피처 테스트
금지
product_interface→network_implementation— 소비자는 interface만cart_implementation→network_implementation— 다른 피처의 구현체 직접 참조auth_presentation→storage_implementation— 비-composition root
implementation을 직접 참조할 수 있는 모듈은 ProjectOptions.compositionRoots로 지정합니다. 기본값은 ['app']입니다. 멀티-앱 구조에서는 여러 값을 선언할 수 있습니다.
options: const ProjectOptions(
compositionRoots: ['app', 'dev_app'], // 여러 composition root 지정 가능
),Testing 레이어 격리
testing은 Fake·Mock·Stub을 담는 전용 패키지입니다. 이 레이어의 존재 의미는 "테스트 더블을 프로덕션 코드 없이 재사용할 수 있는 단위로 배포한다"는 것입니다. 반대로 말하면, 프로덕션 코드에 testing이 유입되는 순간 이 격리는 깨집니다.
testing이 포함되면 릴리즈 빌드에 테스트용 코드가 포함됩니다.허용
network_tests→network_testing— 테스트 코드에서 Mock 사용network_example→network_testing— 데모 앱에서 Fake 사용product_tests→product_testing— 피처 테스트에서 Fake 사용
금지
app→network_testing— 프로덕션 앱에 Mock 유입cart_implementation→network_testing— 프로덕션 구현체에 Fake 유입
Example 독립성
example은 의존성 그래프의 리프 노드입니다. 피처를 앱 없이 독립 실행하여 확인하는 진입점이며, 어떤 모듈도 이를 의존성으로 가져서는 안 됩니다. example이 다른 모듈의 의존성이 되는 순간 프로덕션 빌드에 데모 코드가 포함됩니다.
example은 항상 의존성 화살표를 받는 쪽(수신자)입니다. 화살표를 내보내는 쪽(공급자)이 될 수 없습니다.금지
app→network_example— 프로덕션 앱이 데모 앱에 의존product_implementation→network_example— 구현체가 데모에 의존cart_tests→product_example— 테스트가 다른 피처 데모에 의존
Clean Architecture
Clean Architecture는 Robert C. Martin(Uncle Bob)이 정의한 아키텍처 원칙으로, 비즈니스 로직을 UI·데이터베이스·프레임워크로부터 완전히 분리하는 것을 목표로 합니다. Flutter에서는 피처의 비즈니스 규칙(domain)이 HTTP 클라이언트나 Flutter 위젯이 무엇인지 알지 못하도록 설계합니다.
핵심은 의존성 방향입니다 — 모든 화살표는 domain을 향합니다. domain은 외부를 전혀 모릅니다. data와 presentation이 domain에 의존하고, domain은 아무것에도 의존하지 않습니다.
레이어 구조
장점
- 비즈니스 로직 독립성 — domain 레이어는 Flutter SDK 없이 순수 Dart로 테스트 가능.
- 데이터 소스 교체 — REST API → GraphQL, SQLite → Hive 교체 시 data 레이어만 변경.
- 명확한 책임 분리 — "이 코드가 어디 있어야 하는가?" 질문에 항상 명확한 답이 있음.
- 테스트 용이성 — domain은 mock 없이 테스트, presentation은 Repository mock으로 테스트.
레이어 방향 강제
Clean Architecture에서 가장 흔히 발생하는 실수는 역방향 의존성입니다. 바쁜 개발 중에 domain이 HTTP 클라이언트를 직접 import하거나, data가 UI 위젯을 알게 되는 일이 일어납니다. Flutist는 모듈 이름의 suffix(domain·data·presentation)를 기준으로 같은 피처 내의 방향 위반을 generate 시점에 자동 감지합니다.
허용 (올바른 방향)
weather_data→weather_domain— data가 domain 계약을 구현weather_presentation→weather_domain— UI가 domain UseCase 사용weather_presentation→weather_data— presentation이 data에 의존 (허용)
금지 (역방향)
weather_domain→weather_data— domain이 구현체를 알게 됨weather_domain→weather_presentation— domain이 UI를 알게 됨weather_data→weather_presentation— data가 UI를 알게 됨
Flutist는 project.dart의 modules 선언을 기준으로 검사합니다. 실제 Dart import가 아닌 모듈 의존성 선언 레벨에서 방향을 강제합니다.
순환 의존성
순환 의존성은 두 아키텍처 모두에서 금지됩니다. A → B → C → A 형태의 순환이 발생하면 빌드 시스템이 의존성 순서를 결정할 수 없고, 모듈을 독립적으로 테스트하거나 배포하는 것이 불가능해집니다. Flutist는 DFS(깊이 우선 탐색) 기반으로 모든 모듈 의존성 그래프를 순회하며 순환을 탐지합니다.
금지
auth_domain → utils → auth_domainproduct_interface → cart_interface → product_interfaceA → B → C → A(간접 순환)
순환이 의심될 때는 flutist graph --format ascii로 의존성 방향을 시각화하여 확인하세요.
strictMode
위에서 설명한 모든 규칙은 flutist generate·flutist check 실행 시 항상 검사됩니다. strictMode는 위반을 발견했을 때 중단할지 경고만 출력할지를 제어합니다. 기존 프로젝트를 Flutist로 마이그레이션하는 기간 동안 false로 설정하면 규칙을 위반한 채로 진행하면서 점진적으로 수정할 수 있습니다.
| 설정 | 위반 시 동작 | 권장 용도 |
|---|---|---|
true (기본) | generate 즉시 중단, exit 1 | 일반 개발, CI/CD |
false | 경고 출력 후 계속, exit 0 | 기존 프로젝트 마이그레이션 |
options: const ProjectOptions(
strictMode: false, // 마이그레이션 기간 동안 임시 비활성화
compositionRoots: ['app'],
),strictMode: false는 마이그레이션을 위한 임시 설정입니다. 완료 후 반드시 true로 되돌리세요.
Scaffold Templates
템플릿으로 저장하고 flutist scaffold로 반복 작업을 자동화합니다.
템플릿 변수
| 변수 | 입력: login_feature | 출력 |
|---|---|---|
{{name | snake_case}} | login_feature | login_feature |
{{name | pascal_case}} | LoginFeature | |
{{name | camel_case}} | loginFeature | |
{{name | upper_case}} | LOGIN_FEATURE |
template.yaml 구조
description: "BLoC Feature Template"
attributes:
- name: name
required: true
- name: path
required: false
default: "lib/features"
items:
- type: file
path: "{{path}}/{{name | snake_case}}/{{name | snake_case}}_bloc.dart"
templatePath: "bloc.dart.template"
- type: string
path: "{{path}}/{{name | snake_case}}/README.md"
contents: |
# {{name | pascal_case}}item 타입
| 타입 | 설명 |
|---|---|
file | .template 파일을 읽어 변수 치환 후 생성 |
string | 인라인 내용으로 파일 생성 |
directory | 템플릿 디렉토리 전체 복사 |
실전 예시: BLoC Feature 템플릿
import 'package:bloc/bloc.dart';
part '{{name | snake_case}}_event.dart';
part '{{name | snake_case}}_state.dart';
class {{name | pascal_case}}Bloc
extends Bloc<{{name | pascal_case}}Event, {{name | pascal_case}}State> {
{{name | pascal_case}}Bloc() : super(const {{name | pascal_case}}Initial()) {
on<{{name | pascal_case}}Started>(_onStarted);
}
Future<void> _onStarted({{name | pascal_case}}Started event, Emitter<{{name | pascal_case}}State> emit) async {}
}part of '{{name | snake_case}}_bloc.dart';
sealed class {{name | pascal_case}}Event { const {{name | pascal_case}}Event(); }
final class {{name | pascal_case}}Started extends {{name | pascal_case}}Event {
const {{name | pascal_case}}Started();
}part of '{{name | snake_case}}_bloc.dart';
sealed class {{name | pascal_case}}State { const {{name | pascal_case}}State(); }
final class {{name | pascal_case}}Initial extends {{name | pascal_case}}State { const {{name | pascal_case}}Initial(); }
final class {{name | pascal_case}}Loading extends {{name | pascal_case}}State { const {{name | pascal_case}}Loading(); }
final class {{name | pascal_case}}Loaded<T> extends {{name | pascal_case}}State {
final T data; const {{name | pascal_case}}Loaded(this.data);
}
final class {{name | pascal_case}}Error extends {{name | pascal_case}}State {
final String message; const {{name | pascal_case}}Error(this.message);
}$ flutist scaffold bloc_feature --name login --path lib/features
✓ lib/features/login/login_bloc.dart
✓ lib/features/login/login_event.dart
✓ lib/features/login/login_state.dart실전 예시: Riverpod Notifier 템플릿
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '{{name | snake_case}}_state.dart';
part '{{name | snake_case}}_notifier.g.dart';
@riverpod
class {{name | pascal_case}}Notifier extends _${{name | pascal_case}}Notifier {
@override
{{name | pascal_case}}State build() => const {{name | pascal_case}}State.initial();
Future<void> load() async {
state = const {{name | pascal_case}}State.loading();
try {
state = const {{name | pascal_case}}State.loaded(null);
} catch (e) {
state = {{name | pascal_case}}State.error(e.toString());
}
}
}import 'package:freezed_annotation/freezed_annotation.dart';
part '{{name | snake_case}}_state.freezed.dart';
@freezed
class {{name | pascal_case}}State with _${{name | pascal_case}}State {
const factory {{name | pascal_case}}State.initial() = _Initial;
const factory {{name | pascal_case}}State.loading() = _Loading;
const factory {{name | pascal_case}}State.loaded(dynamic data) = _Loaded;
const factory {{name | pascal_case}}State.error(String msg) = _Error;
}