GetX State Management Patterns について
GetX State Management Patternsは、GetXを使うFlutter開発者向けのコーディングスキルです。リアクティブ状態、依存性注入、ナビゲーション、コントローラーのライフサイクルを扱い、ビジネスロジックを分離する方法も示します。
主な機能
- 01リアクティブ変数とウィジェット
- 02Bindingsによる依存性注入
- 03ルート遷移と引数の受け渡し
- 04コントローラーのライフサイクル指針
- 05スナックバーとダイアログ
- 06状態管理のベストプラクティス
活用シーン
- 01Flutterウィジェットにリアクティブな値を表示
- 02Bindingsでコントローラーやサービスを登録
- 03名前付きまたは直接指定したルートへ遷移
- 04ビジネスロジックをコントローラーから分離
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ kaakati/ rails-enterprise-dev --skill getx-state-management-patternsインストール後、Claude Code で/getx-state-management-patternsを入力して読み込まれたか確認します。
詳細
GetX の状態管理パターン
リアクティブな状態管理
リアクティブ変数
class UserController extends GetxController {
// Simple reactive variable
final count = 0.obs;
// Complex reactive variable
final user = Rx<User?>(null);
// Reactive list
final users = <User>[].obs;
// Reactive map
final settings = <String, dynamic>{}.obs;
// Getters (recommended)
int get countValue => count.value;
User? get currentUser => user.value;
}
リアクティブウィジェット
// Option 1: Obx (lightweight)
Obx(() => Text(controller.count.toString()))
// Option 2: GetX (with dependency injection)
GetX<UserController>(
builder: (controller) => Text(controller.user?.name ?? 'Loading...'),
)
// Option 3: GetBuilder (no reactive variables needed)
GetBuilder<UserController>(
builder: (controller) => Text(controller.userName),
)
依存性注入
登録方法
// Immediate instance
Get.put(UserController());
// Lazy instance (created when first used)
Get.lazyPut(() => UserController());
// Singleton (persists across routes)
Get.putAsync(() => StorageService().init());
// Fenix (recreates when route is accessed again)
Get.lazyPut(() => UserController(), fenix: true);
バインディングパターン
class UserBinding extends Bindings {
@override
void dependencies() {
// Data sources
Get.lazyPut(() => UserProvider(Get.find()));
Get.lazyPut(() => UserLocalSource(Get.find()));
// Repository
Get.lazyPut<UserRepository>(
() => UserRepositoryImpl(Get.find(), Get.find()),
);
// Use cases
Get.lazyPut(() => GetUser(Get.find()));
// Controller
Get.lazyPut(() => UserController(getUserUseCase: Get.find()));
}
}
ナビゲーション
基本的なナビゲーション
// Navigate to route
Get.to(() => ProfilePage());
// Navigate with name
Get.toNamed('/profile');
// Navigate and remove previous
Get.off(() => LoginPage());
// Navigate and remove all previous
Get.offAll(() => HomePage());
// Go back
Get.back();
// Go back with result
Get.back(result: user);
引数を使ったナビゲーション
// Send arguments
Get.toNamed('/profile', arguments: {'id': '123'});
// Receive arguments
final args = Get.arguments as Map<String, dynamic>;
final id = args['id'];
ルート設定
class AppRoutes {
static const initial = '/splash';
static final routes = [
GetPage(
name: '/splash',
page: () => SplashPage(),
binding: SplashBinding(),
),
GetPage(
name: '/login',
page: () => LoginPage(),
binding: AuthBinding(),
transition: Transition.fadeIn,
),
GetPage(
name: '/home',
page: () => HomePage(),
binding: HomeBinding(),
middlewares: [AuthMiddleware()],
),
];
}
コントローラーのライフサイクル
class UserController extends GetxController {
@override
void onInit() {
super.onInit();
// Called immediately after controller is allocated
loadUser();
}
@override
void onReady() {
super.onReady();
// Called after widget is rendered
analytics.logScreenView();
}
@override
void onClose() {
// Clean up resources
_subscription.cancel();
super.onClose();
}
}
スナックバーとダイアログ
// Snackbar
Get.snackbar(
'Success',
'User created successfully',
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.green,
);
// Dialog
Get.dialog(
AlertDialog(
title: Text('Confirm'),
content: Text('Are you sure?'),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text('Cancel'),
),
TextButton(
onPressed: () {
deleteUser();
Get.back();
},
child: Text('Delete'),
),
],
),
);
// Bottom sheet
Get.bottomSheet(
Container(
child: Column(
children: [
ListTile(
title: Text('Option 1'),
onTap: () => Get.back(result: 1),
),
],
),
),
);
ベストプラクティス
✅ 推奨事項
- 依存性注入にはバインディングを使用します
- 非公開のリアクティブ変数と公開ゲッターを使用します
onClose()でリソースをクリーンアップします- 意味のあるコントローラー名を使用します(例:
Controller1ではなくUserController) - ビジネスロジックはコントローラーではなくユースケースに記述します
❌ 禁止事項
- ウィジェット内でコントローラーを直接作成しないでください
- コントローラーにビジネスロジックを記述しないでください
- ストリームやサブスクリプションの破棄を忘れないでください
- グローバル状態を過剰に使用しないでください
- 循環依存を作成しないでください
避けるべきアンチパターン
// ❌ BAD: Business logic in controller
class UserController extends GetxController {
Future<void> createUser(String name, String email) async {
// Direct API call in controller
final response = await http.post(...);
// Business logic in controller
if (response.statusCode == 201) {
user.value = User.fromJson(response.body);
}
}
}
// ✅ GOOD: Delegate to use case
class UserController extends GetxController {
final CreateUser createUserUseCase;
Future<void> createUser(String name, String email) async {
final result = await createUserUseCase(name, email);
result.fold(
(failure) => _handleError(failure),
(user) => user.value = user,
);
}
}
コメント
Markdown 対応