body method

  1. @override
String body(
  1. String baseName,
  2. String className
)
override

Defines the actual body code. path is passed relative to lib, baseName is the filename, and className is the filename converted to Pascal case.

実際の本体コードを定義します。pathlibからの相対パス、baseNameにファイル名が渡され、classNameにファイル名をパスカルケースに変換した値が渡されます。

Implementation

@override
String body(String baseName, String className) {
  return """
`Firebase Remote Config`は下記のように利用する。

## 概要

$excerpt

条件分岐により、ユーザーセグメント、プラットフォーム、アプリバージョンごとに異なる設定値を配信できます。

## 設定方法

### katana.yamlを使用する場合(推奨)

1. `katana.yaml`に下記の設定を追加。

  ```yaml
  # katana.yaml

  # Enable Firebase Remote Config.
  # Firebase Remote Configを有効にします。
  firebase:
    project_id: your-project-id
    remote_config:
      enable: true # Remote Configを利用する場合false -> trueに変更
  ```

2. 下記のコマンドを実行して設定を適用。

  ```bash
  katana apply
  ```

  この方法により、自動的に`masamune_model_firebase_remote_config`パッケージがインストールされ、設定が完了します。

3. `lib/main.dart`で`FirebaseRemoteConfigModelAdapter`を設定。

  ```dart
  // lib/main.dart

  import 'package:masamune/masamune.dart';
  import 'package:masamune_model_firebase_remote_config/masamune_model_firebase_remote_config.dart';

  final modelAdapter = FirebaseRemoteConfigModelAdapter(
    options: DefaultFirebaseOptions.currentPlatform,
    initialValue: {
      "feature_enabled": false,
      "api_endpoint": "https://api.example.com",
      "max_items": 10,
    },
    minimumFetchInterval: Duration(minutes: 30),  // キャッシュ期間
  );

  void main() {
    runMasamuneApp(
      appRef: appRef,
      modelAdapter: modelAdapter,
      (appRef, _) => MasamuneApp(
        appRef: appRef,
        home: HomePage(),
      ),
    );
  }
  ```

### 手動でパッケージを追加する場合

1. パッケージをプロジェクトに追加。

  ```bash
  flutter pub add masamune_model_firebase_remote_config
  ```

2. `lib/main.dart`で`FirebaseRemoteConfigModelAdapter`を設定。

  ```dart
  // lib/main.dart

  import 'package:masamune/masamune.dart';
  import 'package:masamune_model_firebase_remote_config/masamune_model_firebase_remote_config.dart';

  final modelAdapter = FirebaseRemoteConfigModelAdapter(
    options: DefaultFirebaseOptions.currentPlatform,
    initialValue: {
      "feature_enabled": false,
      "api_endpoint": "https://api.example.com",
      "max_items": 10,
    },
    minimumFetchInterval: Duration(minutes: 30),
  );

  void main() {
    runMasamuneApp(
      appRef: appRef,
      modelAdapter: modelAdapter,
      (appRef, _) => MasamuneApp(
        appRef: appRef,
        home: HomePage(),
      ),
    );
  }
  ```

## 利用方法

### Remote Config値の取得

Remote Configの値をMasamuneモデルとして読み込みます。

```dart
import 'package:masamune/masamune.dart';
import 'package:masamune_model_firebase_remote_config/masamune_model_firebase_remote_config.dart';

class MyPage extends PageScopedWidget {
@override
Widget build(BuildContext context, PageRef ref) {
  // Remote Configをドキュメントとして読み込み
  final config = ref.app.model(
    FirebaseRemoteConfigModel.document(),
  )..load();

  return Scaffold(
    body: Column(
      children: [
        // 設定値へのアクセス
        Text("Feature: \${config.value.get<bool>("feature_enabled")}"),
        Text("Endpoint: \${config.value.get<String>("api_endpoint")}"),
        Text("Max Items: \${config.value.get<int>("max_items")}"),
      ],
    ),
  );
}
}
```

### 条件付き設定値の活用

Remote Configで設定した条件に応じて、異なる値を取得できます。

```dart
// 機能フラグによる機能の有効化
final config = ref.app.model(FirebaseRemoteConfigModel.document())..load();

if (config.value.get<bool>("new_feature_enabled", false)) {
// 新機能を表示
return NewFeatureWidget();
} else {
// 従来の機能を表示
return LegacyFeatureWidget();
}
```

### デフォルト値の設定

`initialValue`でデフォルト値を設定することで、Remote Configが利用できない場合でもアプリが動作します。

```dart
final modelAdapter = FirebaseRemoteConfigModelAdapter(
options: DefaultFirebaseOptions.currentPlatform,
initialValue: {
  "welcome_message": "Welcome!",
  "api_timeout_seconds": 30,
  "max_retry_count": 3,
  "feature_flags": {
    "dark_mode": false,
    "beta_features": false,
  },
},
minimumFetchInterval: Duration(hours: 1),
);
```

## Firebase Consoleでの設定

1. Firebase Console → Remote Configを開く
2. パラメータを追加(`initialValue`のキーと一致させる)
3. 条件を設定(オプション):
 - プラットフォーム(iOS、Android、Web)
 - アプリバージョン
 - ユーザープロパティ
 - 国/地域
4. 値を設定して公開

### 条件の例

```
条件名: iOS Users
条件: app.platform == 'ios'
api_endpoint: https://ios-api.example.com

条件名: Beta Users
条件: user.beta_tester in ['true']
new_feature_enabled: true

条件名: Version 2.0+
条件: app.version >= '2.0.0'
max_items: 20
```

## 重要な注意事項

### 読み取り専用

Remote Configは**読み取り専用**です。`save()`や`delete()`を呼び出すと`UnsupportedError`がスローされます。

値の更新はFirebase ConsoleまたはRemote Config REST APIを使用してください。

### キャッシュ動作

`minimumFetchInterval`で指定した期間はキャッシュされた値が使用されます。

- 開発時: `Duration.zero`を指定して常に最新値を取得
- 本番環境: `Duration(hours: 12)`など、適切な間隔を設定

```dart
// 開発時
minimumFetchInterval: Duration.zero,

// 本番環境
minimumFetchInterval: Duration(hours: 12),
```

### フェッチとアクティブ化

`load()`を呼び出すたびに、自動的に`fetchAndActivate()`が実行されます。

- フェッチに失敗した場合はキャッシュ値が使用されます
- デフォルト値(`initialValue`)は常に利用可能です

## 実装例: A/Bテスト

```dart
class ProductPage extends PageScopedWidget {
@override
Widget build(BuildContext context, PageRef ref) {
  final config = ref.app.model(FirebaseRemoteConfigModel.document())..load();
  final buttonColor = config.value.get<String>("button_color", "blue");

  return Scaffold(
    body: Center(
      child: ElevatedButton(
        style: ElevatedButton.styleFrom(
          backgroundColor: buttonColor == "red" ? Colors.red : Colors.blue,
        ),
        child: Text("購入する"),
        onPressed: () {
          // 購入処理
        },
      ),
    ),
  );
}
}
```

## 実装例: 段階的ロールアウト

新機能を特定のユーザーにのみ公開する場合:

```dart
class HomePage extends PageScopedWidget {
@override
Widget build(BuildContext context, PageRef ref) {
  final config = ref.app.model(FirebaseRemoteConfigModel.document())..load();
  final rolloutPercentage = config.value.get<int>("new_ui_rollout", 0);

  // ユーザーIDのハッシュ値でランダムに振り分け
  final userId = ref.app.auth.value?.uid ?? "";
  final userHash = userId.hashCode % 100;

  if (userHash < rolloutPercentage) {
    return NewUIHomePage();
  } else {
    return OldUIHomePage();
  }
}
}
```

## 実装例: メンテナンスモード

アプリ全体をメンテナンスモードにする場合:

```dart
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
  return MasamuneApp(
    home: AppScopedWidget(
      (context, ref) {
        final config = ref.app.model(
          FirebaseRemoteConfigModel.document(),
        )..load();

        if (config.value.get<bool>("maintenance_mode", false)) {
          return MaintenancePage(
            message: config.value.get<String>(
              "maintenance_message",
              "現在メンテナンス中です",
            ),
          );
        }

        return HomePage();
      },
    ),
  );
}
}
```

### Tips

- Remote Configは読み取り専用のため、値の更新はFirebase Consoleで行う
- 開発時は`minimumFetchInterval: Duration.zero`で常に最新値を取得
- `initialValue`で必ずデフォルト値を設定し、オフライン時の動作を保証
- A/Bテストや段階的ロールアウトに活用できる
- メンテナンスモードの切り替えに利用すると、アプリの再リリースなしで対応可能
- Firebase Consoleで条件を設定して、プラットフォームやユーザー属性ごとに異なる値を配信
- `fetchAndActivate()`は`load()`時に自動実行されるため、手動で呼び出す必要なし
""";
}