Cannot retrieve the status of the internet connection when the app is re-launched or hot restarted with internet off
Asked Answered
W

2

2

I am trying to use the BLoC library with the connectivity_plus plugin in Flutter. I have followed this post and this post to set up an Internet Cubit for the project. The code works fine when the app is started with the internet connection turned on.

However, with the internet connection turned off, if I kill the app and re-launch it or do a hot restart, the CircularProgressIndicator() is shown instead of Text("Internet Disconnected").

Turning the internet back on correctly shows Text("Internet Connected") widget. After this, If I turn off the internet connection again, this time around it correctly shows the Text("Internet Disconnected") widget.

Also, emitInternetDisconnected is not caught as an exception in the try catch block to update the app's state.

The problem with the CircularProgressIndicator() being always displayed with the internet disconnected occurs only when the app is re-launched or hot restarted. I cannot figure out the bug in my code. What should I do to fix my code? Thanks

This is the code in the internet_cubit.dart file

import 'dart:async';
import 'dart:io';

import 'package:bloc/bloc.dart';
import 'package:connectivity_plus/connectivity_plus.dart';

import 'internet_enum.dart';

part 'internet_state.dart';

class InternetCubit extends Cubit<InternetState> {
  final Connectivity? connectivity;
  StreamSubscription? connectivityStreamSubscription;

  InternetCubit({required this.connectivity}) : super(InternetLoading()) {
    monitorInternetConnection();
  }

  void monitorInternetConnection() async {
    connectivityStreamSubscription =
        connectivity!.onConnectivityChanged.listen((connectivityResult) async {
      try {
        final result = await InternetAddress.lookup("example.com");
        if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
          if (connectivityResult == ConnectivityResult.wifi) {
            emitInternetConnected(ConnectionType.WiFi);
          } else if (connectivityResult == ConnectivityResult.mobile) {
            emitInternetConnected(ConnectionType.Mobile);
          } else if (connectivityResult == ConnectivityResult.none) {
            emitInternetDisconnected();
          }
        }
      } on SocketException catch (_) {
        emitInternetDisconnected();
      }
    });
  }

  void emitInternetConnected(ConnectionType _connectionType) =>
      emit(InternetConnected(connectionType: _connectionType));

  void emitInternetDisconnected() => emit(InternetDisconnected());

  @override
  Future<void> close() async {
    connectivityStreamSubscription!.cancel();
    return super.close();
  }
}

This is the code in the internet_state.dart file

part of 'internet_cubit.dart';

abstract class InternetState {}

class InternetLoading extends InternetState {}

class InternetConnected extends InternetState {
  final ConnectionType? connectionType;

  InternetConnected({required this.connectionType});
}

class InternetDisconnected extends InternetState {}

This is the code in my main.dart file

import 'package:flutter/material.dart';

import 'package:flutter_bloc/flutter_bloc.dart';

import 'package:connectivity_plus/connectivity_plus.dart';

import 'internet_cubit.dart';

void main() {
  runApp(
    BlocProvider<InternetCubit>(
      create: (_) => InternetCubit(connectivity: Connectivity()),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter BLoC Demo',
      home: SafeArea(
        child: Scaffold(
          appBar: AppBar(
            title: Text("Flutter BLoC Demo"),
            centerTitle: true,
            backgroundColor: Colors.blue[900],
          ),
          body: Center(
            child: Builder(
              builder: (context) {
                return MaterialButton(
                    onPressed: () {
                      Navigator.push(context, MaterialPageRoute(builder: (context) => ScreenOne()));
                    },
                    color: Colors.black,
                    textColor: Colors.white,
                    child: Text("Screen 1"));
              },
            ),
          ),
        ),
      ),
    );
  }
}

class ScreenOne extends StatelessWidget {
  const ScreenOne({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        appBar: AppBar(
          title: Text("Flutter BLoC Demo"),
          centerTitle: true,
          backgroundColor: Colors.blue[900],
        ),
        body: Center(
          child: BlocBuilder<InternetCubit, InternetState>(
            builder: (_, state) {
              if (state is InternetDisconnected) {
                return Text("Internet disconnected");
              } else if (state is InternetConnected) {
                return Text("Internet connected");
              }
              return CircularProgressIndicator();
            },
          ),
        ),
      ),
    );
  }
}

Wanonah answered 3/8, 2021 at 11:59 Comment(0)
W
1

I suspected the problem to lie with my Internet connection at the network layer, so I tried a different approach. I will post my solution here so that it can be useful to someone with a similar problem.

First, I modified the internet_cubit.dart and internet_state.dart files like so:

import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:connectivity_plus/connectivity_plus.dart';

import 'internet_enum.dart';

part 'internet_state.dart';

class InternetCubit extends Cubit<InternetConnectionTypeState> {
  final Connectivity? connectivity;
  // ignore: cancel_subscriptions
  StreamSubscription? internetConnectionTypeStreamSubscription;

  InternetCubit({required this.connectivity}) : super(InternetConnectionTypeLoading()) {
    monitorConnectionType();
  }

  void monitorConnectionType() async {
    internetConnectionTypeStreamSubscription =
        connectivity!.onConnectivityChanged.listen((connectivityResult) async {
      if (connectivityResult == ConnectivityResult.wifi) {
        emitConnectionType(ConnectionType.WiFi);
      } else if (connectivityResult == ConnectivityResult.mobile) {
        emitConnectionType(ConnectionType.Mobile);
      }
    });
  }

  void emitConnectionType(ConnectionType _connectionType) =>
      emit(InternetConnectionType(connectionType: _connectionType));

  @override
  Future<void> close() async {
    internetConnectionTypeStreamSubscription!.cancel();
    return super.close();
  }
}
part of 'internet_cubit.dart';

abstract class InternetConnectionTypeState {}

class InternetConnectionTypeLoading extends InternetConnectionTypeState {}

class InternetConnectionType extends InternetConnectionTypeState {
  final ConnectionType? connectionType;

  InternetConnectionType({required this.connectionType});
}

This is the internet_enum.dart file:

enum ConnectionType {
  WiFi, Mobile
}

Next, I imported the internet_connection_checker package. I created a new cubit class called ConnectionCheckerCubit. Following the above code as a guide and the documentation for the internet_connection_checker package, here is the code for the connection_cubit.dart and the connection_state.dart file.

import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:internet_connection_checker/internet_connection_checker.dart';

part 'connection_state.dart';

class ConnectionCheckerCubit extends Cubit<ConnectionCheckerState> {
  final InternetConnectionChecker? internetConnectionChecker;

  ConnectionCheckerCubit({required this.internetConnectionChecker}) : super(InternetConnectionLoading()) {
    monitorInternetConnection();
  }

  // ignore: cancel_subscriptions
  StreamSubscription? internetConnectionStreamSubscription;

  void monitorInternetConnection() async {
    internetConnectionStreamSubscription = InternetConnectionChecker().onStatusChange.listen((status) {
      switch (status) {
        case InternetConnectionStatus.connected:
          emitInternetConnectionConnected(InternetConnectionStatus.connected);
          break;
        case InternetConnectionStatus.disconnected:
          emitInternetConnectionDisconnected();
          break;
      }
    });
  }

  void emitInternetConnectionConnected(InternetConnectionStatus _internetConnectionStatus) =>
      emit(InternetConnectionConnected(internetConnectionStatus: _internetConnectionStatus));

  void emitInternetConnectionDisconnected() => emit(InternetConnectionDisconnected());

  @override
  Future<void> close() async {
    internetConnectionStreamSubscription!.cancel();
    return super.close();
  }
}
part of 'connection_cubit.dart';

abstract class ConnectionCheckerState {}

class InternetConnectionLoading extends ConnectionCheckerState {}

class InternetConnectionConnected extends ConnectionCheckerState {
  final InternetConnectionStatus? internetConnectionStatus;

  InternetConnectionConnected({required this.internetConnectionStatus});
}

class InternetConnectionDisconnected extends ConnectionCheckerState {}

In the main.dart file, I used a MultiBlocProvider in the main() function as a wrapper for runApp. In the ScreenOne widget, I used a BlocBuilder widget for the InternetCubit and used context.watch<ConnectionCheckerCubit>().state to monitor the state of the ConnectionCheckerCubit. Also, I have added a AppBlocObserver class for debugging purposes. Here is the code for the main.dart file:

import 'dart:developer';

import 'package:internet_connection_checker/internet_connection_checker.dart';
import 'package:flutter/material.dart';

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:bloc/bloc.dart';

import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter_bloc_api/internet_enum.dart';
import 'connection_cubit.dart';

import 'internet_cubit.dart';

void main() {

  Bloc.observer = AppBlocObserver();
  runApp(
    MultiBlocProvider(
      providers: [
        BlocProvider<ConnectionCheckerCubit>(
          create: (_) => ConnectionCheckerCubit(internetConnectionChecker: InternetConnectionChecker()),
        ),
        BlocProvider<InternetCubit>(
          create: (_) => InternetCubit(connectivity: Connectivity()),
        ),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter BLoC Demo',
      home: SafeArea(
        child: Scaffold(
          appBar: AppBar(
            title: Text("Flutter BLoC Demo"),
            centerTitle: true,
            backgroundColor: Colors.blue[900],
          ),
          body: Center(
            child: Builder(
              builder: (context) {
                return MaterialButton(
                    onPressed: () {
                      Navigator.push(context, MaterialPageRoute(builder: (context) => ScreenOne()));
                    },
                    color: Colors.black,
                    textColor: Colors.white,
                    child: Text("Screen 1"));
              },
            ),
          ),
        ),
      ),
    );
  }
}

class ScreenOne extends StatelessWidget {
  const ScreenOne({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        appBar: AppBar(
          title: Text("Flutter BLoC Demo"),
          centerTitle: true,
          backgroundColor: Colors.blue[900],
        ),
        body: Center(
          child: Builder(
            builder: (context) {
              final connectionState = context.watch<ConnectionCheckerCubit>().state;
              final internetTypeState = context.watch<InternetCubit>().state;
              if (connectionState is InternetConnectionDisconnected)
                return Text("Internet Disconnected");
              else if (connectionState is InternetConnectionConnected){
                if (internetTypeState is InternetConnectionType && internetTypeState.connectionType == ConnectionType.WiFi)
                  return Text("WiFi");
                else
                  return Text("Mobile");
              }
              return CircularProgressIndicator();
            }
          )
        ),
      ),
    );
  }
}

class AppBlocObserver extends BlocObserver {
  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    log('onChange: ${bloc.runtimeType}, ${bloc.state} \nCurrent state: ${change.currentState}\nNext state: ${change.nextState}');
  }

  @override
  void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
    log('onError(${bloc.runtimeType}, ${bloc.state}, $error, $stackTrace)');
    super.onError(bloc, error, stackTrace);
  }

  @override
  void onEvent(Bloc bloc, Object? event) {
    log('onEvent(${bloc.state}, ${bloc.runtimeType}, $event)');
    super.onEvent(bloc, event);
  }

  @override
  void onTransition(Bloc bloc, Transition transition) {
    log('onTransition(${bloc.state}, ${bloc.runtimeType}, ${transition.currentState}, ${transition.nextState})');
    super.onTransition(bloc, transition);
  }

  @override
  void onCreate(BlocBase bloc) {
    log('onCreate(${bloc.state}, ${bloc.runtimeType})');
    super.onCreate(bloc);
  }

  @override
  void onClose(BlocBase bloc) {
    log('onTransition(${bloc.state}, ${bloc.runtimeType})');
    super.onClose(bloc);
  }
}

Here is a Github link for more information.

Wanonah answered 5/8, 2021 at 14:21 Comment(0)
E
0

add this library connectivity: ^3.0.6

and try this code in your class:-

_startNetworkTesting(BuildContext context) async {
    
    var result = await (Connectivity().checkConnectivity());
    if (result == ConnectivityResult.none) {
      setState(() {
       //perform your action
      });
    } else if (result == ConnectivityResult.mobile) {
      setState(() {
       //perform your action
      });
    } else if (result == ConnectivityResult.wifi) {
      setState(() {
        //perform your action
      });
    }
  }
Etka answered 3/8, 2021 at 15:15 Comment(1)
The connectivity plugin may be deprecated in the future. As per the documentation, it is recommented to use the connectivity_plus plugin. What is the purpose of passing a BuildContext as a parameter in the function when it is not being used in the function? I prefer to use BLoC as a state management solution rather than setState() for checking internet connectivity in the appWanonah

© 2022 - 2024 — McMap. All rights reserved.