実践!トラブルシューティング

📋 今日の内容
ケーススタディ

ケース1:突然アプリが起動しなくなった
ケース2:ビルドは成功するのに真っ白画面
ケース3:Android版は動くのにiOS版が動かない
ケース4:本番ビルドだけエラーが出る
ケース5:パッケージ更新後に動かなくなった

実際の調査プロセス!

目次

ケース1:突然アプリが起動しなくなった

状況:昨日まで動いていたアプリが、今日突然起動しない

エラーメッセージ:

flutter run
Launching lib/main.dart on sdk gphone64 arm64 in debug mode...
FAILURE: Build failed with an exception.

🔍 調査ステップ1:何が変わったか確認

昨日と今日で何か変えた?

何も変えていません…

でも実は:

  • 他のプロジェクトで作業した
  • Android Studioを更新した
  • macOSを更新した

🔍 調査ステップ2:環境チェック

flutter doctor -v
```

**出力:**
```
[!] Android toolchain - develop for Android devices
    ✗ Android SDK file not found: ~/.android/debug.keystore

原因発見!debug.keystoreが消えている!

✅ 解決方法(段階的に試す)

# debug.keystoreを再生成
keytool -genkey -v -keystore ~/.android/debug.keystore \
  -storepass android -alias androiddebugkey \
  -keypass android -keyalg RSA -keysize 2048 -validity 10000
```

**質問に答える:**
```
姓名: test
組織単位: test
組織: test
都市: test
都道府県: test
国コード: JP

再実行:

flutter run

✅ 解決!

💡 学んだこと

STEP
何も変えていないと思っても、環境は変わっている
STEP
まず flutter doctor で環境確認
STEP
Android関連ファイルは再生成できる

ケース2:ビルドは成功するのに真っ白画面

flutter run
✓ Built build/app/outputs/flutter-apk/app-debug.apk

ビルド成功!でもアプリを開くと真っ白… 😱

🔍 調査ステップ1:ログを確認

# 詳細ログで実行
flutter run -v
```

**ログを見ると:**
```
E/flutter (12345): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] 
Unhandled Exception: ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized.

🔍 調査ステップ2:main.dart を確認

問題のコード:

void main() {
  // SharedPreferences を初期化
  SharedPreferences prefs = await SharedPreferences.getInstance();
  
  runApp(MyApp());
}

エラー!

await を使っているのに async がない
WidgetsFlutterBinding.ensureInitialized() がない

✅ 解決方法(段階的に試す)
修正後:

void main() async {  // async を追加
  // Flutter の初期化を保証
  WidgetsFlutterBinding.ensureInitialized();  // これを追加
  
  // SharedPreferences を初期化
  SharedPreferences prefs = await SharedPreferences.getInstance();
  
  runApp(MyApp());
}

💡 学んだこと

STEP
main() で async を使う場合は必ず ensureInitialized()
STEP
真っ白画面は初期化エラーの可能性が高い
STEP
flutter run -v で詳細ログを確認

ケース3:Android版は動くのにiOS版が動かない

Androidエミュレータ:✅ 完璧に動く
iOSシミュレータ:❌ 起動直後にクラッシュ

🔍 調査ステップ1:iOS特有の設定を確認

# iOSで実行
flutter run -d iPhone

# エラー:
[VERBOSE-2:platform_view_layer.cc] Trying to embed a platform view but the 
PrerollContext does not support embedding

🔍 調査ステップ2:Info.plist を確認

問題:パーミッションの説明が抜けている

open ios/Runner/Info.plist

カメラを使っているのに:

open ios/Runner/Info.plist

✅ 解決方法
Info.plist に追加:

<?xml version="1.0" encoding="UTF-8"?>
<dict>
    <!-- 既存の設定 -->
    
    <!-- これらを追加 -->
    <key>NSCameraUsageDescription</key>
    <string>イベントの写真を撮影するためにカメラを使用します</string>
    
    <key>NSPhotoLibraryUsageDescription</key>
    <string>イベントの写真を選択するためにフォトライブラリにアクセスします</string>
</dict>

💡 学んだこと

STEP
iOSはパーミッションの説明が必須
STEP
Androidで動いてもiOSで動くとは限らない
STEP
各プラットフォームの要件を確認する

ケース4:本番ビルドだけエラーが出る

状況

flutter run --debug
✅ デバッグモード:完璧に動く

flutter build apk --release
❌ リリースビルド:エラー

🔍 調査ステップ1:エラーメッセージを確認

flutter build apk --release -v
```

**エラー:**
```
MissingPluginException(No implementation found for method getAll on channel plugins.flutter.io/shared_preferences)

🔍調査ステップ2:ProGuardの確認
原因:難読化で必要なコードが削除された
android/app/build.gradle.kts を確認:

buildTypes {
    release {
        isMinifyEnabled = true  // 難読化が有効
        proguardFiles(...)
    }
}

✅ 解決方法
android/app/proguard-rules.pro を作成:

# Flutter関連
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }

# Shared Preferences
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer

再ビルド:

flutter clean
flutter build apk --release

💡 学んだこと

STEP
デバッグとリリースで動作が違うことがある
STEP
難読化(ProGuard)が原因のことが多い
STEP
proguard-rules.pro で除外設定

ケース5:パッケージ更新後に動かなくなった

状況

flutter pub upgrade
flutter run

エラー:
The method 'xxx' was called on null.

昨日まで動いていたのに… 😱

🔍 調査ステップ1:何が更新されたか確認

git diff pubspec.lock

出力

  http:
-   version: "1.1.0"
+   version: "1.2.0"

  image_picker:
-   version: "1.0.4"
+   version: "1.0.7"

image_pickerが破壊的変更をしている可能性!

🔍 調査ステップ2:CHANGELOGを確認

https://pub.dev/packages/image_picker/changelog
```

**1.0.5で変更:**
```
BREAKING CHANGE: pickImage() の戻り値が変更
- Before: File?
- After: XFile?

✅ 解決方法

コードを修正:

// 古いコード(動かない)
Future<void> _pickImage() async {
  final ImagePicker picker = ImagePicker();
  final File? image = await picker.pickImage(source: ImageSource.gallery);
  //    ↑ File? は使えない
}

// 新しいコード(修正後)
Future<void> _pickImage() async {
  final ImagePicker picker = ImagePicker();
  final XFile? image = await picker.pickImage(source: ImageSource.gallery);
  //    ↑ XFile? に変更
  
  if (image != null) {
    File file = File(image.path);  // File に変換
    setState(() {
      _selectedImage = file;
    });
  }
}

💡 学んだこと

STEP
パッケージ更新は慎重に
STEP
更新前に CHANGELOG を確認
STEP
git で変更履歴を追跡
STEP
問題あればバージョンを戻す

🎯 トラブルシューティングの基本手順

黄金の7ステップ

STEP
何が起こったか正確に把握

→ エラーメッセージをコピー

STEP
何が変わったか確認

→ git diff で確認

STEP
環境チェック

→ flutter doctor

STEP
詳細ログ確認

→ flutter run -v

STEP
Google検索

→ エラーメッセージで検索

STEP
基本のクリーン

→ flutter clean && flutter pub get

STEP
段階的に解決

→ 一つずつ試す

🔧 実践的なデバッグテクニック

テクニック1:分割統治法

問題:どこでエラーが起きているか分からない


dart// コードをコメントアウトして絞り込む
void main() {
runApp(MyApp());

// loadData(); // まずこれをコメントアウト
// initDatabase(); // 次にこれ
}


一つずつ有効化して、どこで壊れるか特定

テクニック2:print デバッグ


Future fetchData() async {
print('=== fetchData 開始 ===');

try {
print('URL: $apiUrl');
final response = await http.get(Uri.parse(apiUrl));
print('Status: ${response.statusCode}');
print('Body: ${response.body}');

return response;

} catch (e) {
print('エラー: $e');
rethrow;
}
}


ログを追って、どこまで実行されたか確認

テクニック3:バージョンを戻す

パッケージ更新後に問題が出たら:

bash# 元に戻す
git checkout pubspec.lock
flutter pub get

動作確認

flutter run

動いたら、一つずつ更新

flutter pub upgrade http # 個別に更新
flutter pub upgrade image_picker

テクニック4:最小再現コードを作る

問題を切り離す:


// 最小限のコード
void main() {
runApp(MaterialApp(
home: Scaffold(
body: Center(
child: Text('テスト'),
),
),
));
}


これが動けば、Flutter環境は問題なし

📊 トラブルシューティング優先度

レベル1:まずこれを試す(90%解決)

bashflutter clean
flutter pub get
flutter run

レベル2:それでもダメなら(5%解決)

# キャッシュクリア
flutter pub cache repair

# Gradleクリア(Android)
cd android && ./gradlew clean && cd ..

# Podクリア(iOS)
cd ios && rm -rf Pods/ && pod install && cd ..

レベル3:詳しく調査(4%解決)

# 詳細ログ
flutter run -v

# 環境確認
flutter doctor -v

# Google検索
"flutter" + "エラーメッセージ"

レベル4:最終手段(1%解決)

# 完全リセット
rm -rf build/
rm -rf .dart_tool/
rm -rf android/.gradle/
rm -rf ios/Pods/
flutter clean
flutter pub get

💡 予防策


① Gitでバージョン管理


bash# こまめにコミット
git add .
git commit -m "機能追加:画像アップロード"

# 問題が出たら戻せる
git log # 履歴確認
git checkout HEAD~1 # 1つ前に戻る

② パッケージ更新は計画的に


bash# 更新前にバックアップ
git commit -am "パッケージ更新前"

# CHANGELOGを確認
# https://pub.dev/packages/[パッケージ名]/changelog

# 一つずつ更新

flutter pub upgrade http
flutter test # テスト

# 問題なければコミット

git add pubspec.lock
git commit -m "http を 1.2.0 に更新"

③ 定期的なメンテナンス

# 週1回
flutter clean
flutter pub get
flutter doctor

# 月1回

flutter upgrade # Flutter自体を更新
flutter pub outdated # 古いパッケージ確認

まとめ:トラブル解決

① パニックにならない

エラーは必ず解決できる
誰かが同じ問題に遭遇している

② 一つずつ試す

複数の変更を同時にしない
何を試したか記録する

③ ログを読む

エラーメッセージは友達
最初と最後をよく読む

④ Google検索を活用

“flutter” + エラーメッセージ
Stack Overflow を確認
GitHub Issues を確認

⑤ コミュニティに質問

Stack Overflow
Flutter Discord
GitHub Issues

🎊 今日のケーススタディまとめ


✅ ケース1:debug.keystore 再生成
✅ ケース2:ensureInitialized() 追加
✅ ケース3:iOS パーミッション設定
✅ ケース4:ProGuard 設定
✅ ケース5:パッケージ更新対応
目次