🍥

【Flutter/connectivity_plus】ネットワーク状況を監視する

2023/12/21に公開

通信を行う iOS/Android アプリを Flutter で開発しています。
インターネットに接続されていない場合にメッセージを表示したかったため、ネットワーク状況を監視するパッケージを利用したので、備忘録として残します。

今回作成したサンプルアプリケーション

https://github.com/ndjndj/flutter_connectivity_sample

要約

  • ネットワーク状況の監視には connectivity_plus パッケージを使用する
  • ConnectivityResult を状態として保持する
  • StreamSubscription を利用して、状態として保持している ConnectivityResult を監視する
  • 保持している状態に応じて表示する Widget を切り替える
  • ConnectivityResult はモバイル通信(mobile)や Wi-Fi 通信(wifi)などがある

connectivity_plus パッケージを追加する

connectivity_plus を利用することで、ネットワーク状況を取得することができます
通常のパッケージ利用と同様の手法で、flutter pub add connectivity_plus をコマンドラインなどで実行するか、pubspec.yaml の dependencies に記述を追加してパッケージを導入します

https://pub.dev/packages/connectivity_plus

サンプルコード

以下のようにネットワーク状況に応じて表示する Widget を切り替えるだけの簡易的なアプリケーションを作成します

  • ネットワークにつながっているとき
    • 通常の画面(connectivity_sample と表示するだけの画面)を表示
  • ネットワークにつながっていないとき
    • 通常の画面操作をすることができないようにアラートを表示する

main.dart

コード全文
import 'package:flutter/material.dart';
import 'package:flutter_connectivity_sample/src/connectivity_sample.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Connectivity Sample',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(
        title: 'Flutter Connectivity Sample'
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(title),
      ),
      body: const ConnectivitySample(
        child: Center(
          child: Text(
            'connectivity sample',
          ),
        ),
      )
    );
  }
}

connectivity_sample.dart

コード全文
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter_connectivity_sample/src/not_connection.dart';

class ConnectivitySample extends StatefulWidget {
  const ConnectivitySample({
    super.key,
    required this.child
  });

  final Widget child;

   
  State<ConnectivitySample> createState() => _State();
}

class _State extends State<ConnectivitySample> {
  ConnectivityResult connectionStatus = ConnectivityResult.none;
  final Connectivity connectivity = Connectivity();
  late StreamSubscription<ConnectivityResult> connectivitySubscription;

   
  void initState() {
    super.initState();
    initConnectivity();
    connectivitySubscription = connectivity.onConnectivityChanged.listen(updateConnectionStatus);
  }

   
  void dispose() {
    connectivitySubscription.cancel();
    super.dispose();
  }

  Future<void> initConnectivity() async {
    late ConnectivityResult result;
    try {
      result = await connectivity.checkConnectivity();
    } on PlatformException catch(_) {
      return;
    }

    if (!mounted) {
      return;
    }
    
    updateConnectionStatus(result);
  }

  Future<void> updateConnectionStatus(ConnectivityResult result) async {
    setState(() {
      connectionStatus = result;
    });
  }

   
  Widget build(BuildContext context) {
    return connectionStatus == ConnectivityResult.none 
    ? const NotConnection()
    : widget.child;
  }
}

not_connection.dart

コード全文
import 'package:flutter/material.dart';

class NotConnection extends StatelessWidget {
  const NotConnection({
    super.key
  });

  
  Widget build(BuildContext context) {
    return const AlertDialog(
      title: Text("not connection"),
      content: SizedBox(
        width: 200,
        height: 200,
        child: Text("network error."),
      ),
    );
  }
}

ConnectivityResult を状態として保持する

ConnectivityResult を状態として保持します
enum で none, mobile, wifi などのステータスがあります
今回は none のみの使用ですが、モバイル通信と Wi-Fi 通信の違いで処理を分けたい場合(表示する画像の画質を変えるなど)は活用できそうです

https://pub.dev/documentation/connectivity_plus/latest/connectivity_plus/ConnectivityResult.html

ConnectivityResult connectionStatus = ConnectivityResult.none;

Future<void> updateConnectionStatus(ConnectivityResult result) async {
  setState(() {
    connectionStatus = result;
  });
}

StreamSubscription を用いて ConnectivityResult を監視する

StreamSubscription を用いて、State として保持している ConnectivityResult を監視します

 
void initState() {
  super.initState();
  initConnectivity();
  connectivitySubscription = connectivity.onConnectivityChanged.listen(updateConnectionStatus);
}

Future<void> updateConnectionStatus(ConnectivityResult result) async {
  setState(() {
    connectionStatus = result;
  });
}

StreamSubscription についてはこちら

https://api.flutter.dev/flutter/dart-async/StreamSubscription-class.html
https://ta-watanabe.hatenablog.com/entry/2021/08/04/234412
https://www.educative.io/answers/what-is-the-streamsubscription-class-in-dart

State に応じて表示する Widget を切り替える

State に応じて、表示する Widget を切り替えるようにします
今回は NotConnection というダイアログを用意しています

 
Widget build(BuildContext context) {
  return connectionStatus == ConnectivityResult.none 
  ? const NotConnection()
  : widget.child;
}

main.dart の Widget に追加する

connectivity_sample.dart には child として Widget を渡せるようにしています
Widget から ConnectivitySample を呼び出し、その child プロパティにネットワークにつながっている状態で表示したい Widget を渡します


Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      title: Text(title),
    ),
    body: const ConnectivitySample(
      child: Center(
        child: Text(
          'connectivity sample',
        ),
      ),
    )
  );
}

実際の動作画面

ネットワークにつながっているとき

ConnectivityResult.none じゃないとき

通信をきる

ネットワークにつながっていないとき

ConnectivityResult.none のとき

再び通信を ON にすれば元の画面に戻ります

参考

パッケージ

https://pub.dev/packages/connectivity_plus
https://pub.dev/documentation/connectivity_plus/latest/index.html

実装について

https://medium.com/vipin-vijayan/network-connectivity-in-flutter-78aa49314340
https://gist.github.com/sbosell/d47b5c4a6fb2be90a686e0fcc95b9bc0

Stream について

https://api.flutter.dev/flutter/dart-async/StreamSubscription-class.html
https://ta-watanabe.hatenablog.com/entry/2021/08/04/234412
https://www.educative.io/answers/what-is-the-streamsubscription-class-in-dart

サンプルアプリケーション

https://github.com/ndjndj/flutter_connectivity_sample

さいごに

今回は表示する Widget を切り替えることでネットワークにつながっていないときはアプリ自体の利用を制限するような実装ですが、一部機能の制限などをしたい場合は状態管理の方法に Riverpod を利用して状態だけを切り出すなど、もう少し工夫が必要そうです

また、connectivity_plus は iOS/Android のほか、他のプラットフォームにも対応しているパッケージなので、Web アプリや iOS/Android 以外のネイティブアプリに同様の仕組みを実装したい場合にも役立つことがわかりました

以上です
ここまで読んでいただきありがとうございました!

Discussion