Flutterアプリ開発中に「Unhandled Exception: Bad state: Future already completed」というエラーに遭遇した経験はありませんか?このエラーは非同期処理に関連して発生することが多く、開発者を悩ませる原因となっています。本記事では、このエラーの原因と具体的な解決方法について詳しく解説します。

エラーの原因

「Future already completed」エラーは、既に完了したFutureに対して再度値を設定しようとした際に発生します。主に以下のような状況で起こります:

1. 同じFutureに対して複数回`Completer.complete()`や`Completer.completeError()`を呼び出した場合

2. 非同期処理の結果を待つ際に、タイムアウトや他の条件によってFutureが既に完了している状態で再度完了させようとした場合

解決方法

このエラーを解決するためには、以下の方法を試してみましょう:

1. Completerの使用を見直す

Completerを使用している場合、complete()メソッドが1回だけ呼び出されていることを確認します。

final completer = Completer<String>();

// 正しい使用法
if (!completer.isCompleted) {
  completer.complete('結果');
}

// 間違った使用法(エラーの原因)
completer.complete('結果1');
completer.complete('結果2'); // エラー発生

2. 非同期処理の制御を改善する

非同期処理を適切に制御し、Futureの状態を常に把握するようにします。

Future<void> fetchData() async {
  try {
    final result = await api.getData();
    // 結果の処理
  } catch (e) {
    // エラー処理
  }
}

3. タイムアウト処理の見直し

タイムアウト処理を実装する際は、Futureの状態を適切に管理します。

Future<String> fetchWithTimeout() async {
  final completer = Completer<String>();
  
  final timer = Timer(Duration(seconds: 5), () {
    if (!completer.isCompleted) {
      completer.completeError(TimeoutException('操作がタイムアウトしました'));
    }
  });

  try {
    final result = await someAsyncOperation();
    if (!completer.isCompleted) {
      completer.complete(result);
    }
  } catch (e) {
    if (!completer.isCompleted) {
      completer.completeError(e);
    }
  } finally {
    timer.cancel();
  }

  return completer.future;
}

4. 状態管理の改善

状態管理ライブラリ(例:Provider, Riverpod, BLoC)を使用して、非同期処理の状態を適切に管理することも効果的です。

class DataNotifier extends ChangeNotifier {
  String _data = '';
  bool _isLoading = false;

  Future<void> fetchData() async {
    if (_isLoading) return; // 既に処理中の場合は何もしない

    _isLoading = true;
    notifyListeners();

    try {
      _data = await api.getData();
    } catch (e) {
      // エラー処理
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }
}

まとめ

「Unhandled Exception: Bad state: Future already completed」エラーは、非同期処理の管理が適切でない場合に発生します。このエラーを防ぐためには、Futureの状態を常に把握し、適切に制御することが重要です。Completerの使用を見直し、非同期処理の制御を改善し、状態管理を適切に行うことで、このエラーを解決し、より安定したFlutterアプリケーションを開発することができます。

エラーの解決に困った際は、上記の方法を試してみてください。また、Flutter開発においては、非同期プログラミングの基礎をしっかりと理解することが、このような問題を未然に防ぐ上で非常に重要です。継続的な学習と実践を通じて、スキルアップを図りましょう。