skip to Main Content

Requirements:

  • Display dialog if internet is unavailable throughout app
  • Pop off the dialog only if retry is clicked and internet is available.

Needs suggestion in:

  • Pop off the dialog only if retry is clicked and internet is available.

Approach:

  • I am listening to stream and if the connection is available display rest of app else dialog. But when retry is clicked nothing is done.

Why ?

  • isDialogDisplayed variable changes is not listened and I can’t have setState there.

main

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp();

  var isDialogDisplayed = false;
  runApp(MaterialApp(
    debugShowCheckedModeBanner: false,
    theme: CustomTheme.customTheme(),
    home: MultiBlocProvider(
        providers: Providers.getProviders,
        child: BlocConsumer<ConnectivityCubit, ConnectivityState>(
            listener: (context, state) {
          });
          if (state == ConnectivityState.disconnected) {
            isDialogDisplayed = true;
          }
          if (state == ConnectivityState.connected &&
              isDialogDisplayed == true) {
            isDialogDisplayed = false;
          }
        }, builder: (context, state) {
          if (state == ConnectivityState.init) {
            return const Scaffold(body: ShimmerHome()); 
          }
          return isDialogDisplayed
              ? Scaffold(
                  body: NoDataHelper(
                        ...
                        onTap: () {
                        if (state == ConnectivityState.connected) {
                          // isDialogDisplayed = false;          // Doesn't work
                        }
                      }),
                )
              : state == ConnectivityState.connected
                  ? MaterialApp.router(
                     ...
                    )
                  : const Scaffold(body: Center(child: ShimmerHome()));
        })),
  ));
}

Cubit

class ConnectivityCubit extends Cubit<ConnectivityState> {
  final Connectivity _connectivity = Connectivity();
  StreamSubscription<ConnectivityResult>? _subscription;
  late Stream<ConnectivityResult> streamValue;

  ConnectivityCubit() : super(ConnectivityState.init) {
    streamValue = _connectivity.onConnectivityChanged;
    _subscription = _connectivity.onConnectivityChanged.listen((result) {
      checkConnectivity(result);
    });
  }

  checkConnectivity(ConnectivityResult result) async {
    if (result == ConnectivityResult.none) {
      emit(ConnectivityState.disconnected);
    } else {
      bool isInternetAvailable = await checkInternetConnectivity();
      if (isInternetAvailable) {
        emit(ConnectivityState.connected);
      } else {
        emit(ConnectivityState.disconnected);
      }
    }
  }

  Future<bool> checkInternetConnectivity() async {
    try {
      final response = await http
          .get(Uri.parse("https://www.google.com"))
          .timeout(Duration(seconds: 10)); // Set a 10-second timeout
      if (response.statusCode == 200) {
        return true;
      } else {
        return false;
      }
    } catch (e) {
      return false;
    }
  }

  @override
  Future<void> close() {
    _subscription?.cancel();
    return super.close();
  }
}

What am I looking for ?

  • Fix would be highly appreciated.
  • I am also open for different architecture and better approach.

2

Answers


  1. I have made a few changes to your logic.

    1. You should only emit state when network is disconnected in case of network change listener subscription.
    2. No need to use BlocListener, you can directly make AlertDialog as body of Scaffold.

    Here is the code I came up with

    import 'dart:async';
    
    import 'package:connectivity_plus/connectivity_plus.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter_bloc/flutter_bloc.dart';
    import 'package:http/http.dart' as http;
    
    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      runApp(
        MultiBlocProvider(
          providers: [BlocProvider(create: (_) => ConnectivityCubit())],
          child: BlocBuilder<ConnectivityCubit, ConnectivityState>(
            builder: (context, state) {
              if (state == ConnectivityState.init) {
                return const MaterialApp(home: Scaffold(body: SizedBox.shrink()));
              } else if (state == ConnectivityState.disconnected) {
                return MaterialApp(
                  home: Scaffold(
                    appBar: AppBar(
                      title: const Text('Connectivity Check'),
                    ),
                    body: AlertDialog(
                      actions: [
                        ElevatedButton(
                          onPressed: () {
                            context.read<ConnectivityCubit>().checkConnectivity();
                          },
                          child: const Text('Retry'),
                        ),
                      ],
                      title: const Text('Internet not available'),
                    ),
                  ),
                );
              } else if (state == ConnectivityState.connected) {
                // repalce this with MaterialApp.router
                return MaterialApp(
                  home: Scaffold(
                    appBar: AppBar(
                      title: const Text('Connectivity Check'),
                    ),
                    body: const Center(child: Text('Connected')),
                  ),
                );
              } else {
                assert(false, "state not mapped");
                return const SizedBox.shrink();
              }
            },
          ),
        ),
      );
    }
    
    enum ConnectivityState {
      init,
      disconnected,
      connected,
    }
    
    class ConnectivityCubit extends Cubit<ConnectivityState> {
      final Connectivity _connectivity = Connectivity();
      StreamSubscription<ConnectivityResult>? _subscription;
      ConnectivityCubit() : super(ConnectivityState.init) {
        checkConnectivity();
        _subscription = _connectivity.onConnectivityChanged.listen((result) {
          if (result == ConnectivityResult.none) {
            emit(ConnectivityState.disconnected);
          }
        });
      }
    
      checkConnectivity() async {
        final result = await _connectivity.checkConnectivity();
        if (result == ConnectivityResult.none) {
          emit(ConnectivityState.disconnected);
        } else {
          bool isInternetAvailable = await checkInternetConnectivity();
          if (isInternetAvailable) {
            emit(ConnectivityState.connected);
          } else {
            emit(ConnectivityState.disconnected);
          }
        }
      }
    
      Future<bool> checkInternetConnectivity() async {
        try {
          final response = await http
              .get(Uri.parse("https://www.google.com"))
              .timeout(const Duration(seconds: 10)); // Set a 10-second timeout
          return response.statusCode == 200;
        } catch (e) {
          return false;
        }
      }
    
      @override
      Future<void> close() {
        _subscription?.cancel();
        return super.close();
      }
    }
    
    
    Login or Signup to reply.
  2. isDialogDisplayed, The state has not listening of this variables value. So when u update its value nothing happens & u r not using setState olso .. there r few ways to fix this.

    1. first one
    void mainnn() async {
      WidgetsFlutterBinding.ensureInitialized();
    
      await Firebase.initializeApp();
    
      final isDialogDisplayed = ValueNotifier<bool>(false);
      runApp(MaterialApp(
        debugShowCheckedModeBanner: false,
        theme: CustomTheme.customTheme(),
        home: MultiBlocProvider(
            providers: Providers.getProviders,
            child: BlocConsumer<ConnectivityCubit, ConnectivityState>(
                listener: (context, state) {
              });
              if (state == ConnectivityState.disconnected) {
                isDialogDisplayed.value = true;
              }
              if (state == ConnectivityState.connected &&
                  isDialogDisplayed.value == true) {
                isDialogDisplayed.value = false;
              }
            }, builder: (context, state) {
              if (state == ConnectivityState.init) {
                return const Scaffold(body: ShimmerHome()); 
              }
              return  ValueListenableBuilder(
                    valueListenable:  isDialogDisplayed,
                    builder: (context, _isDialogDis, _) {
                      return  _isDialogDis
                  ? Scaffold(
                      body: NoDataHelper(
                            ...
                            onTap: () {
                            if (state == ConnectivityState.connected) {
                              isDialogDisplayed.value = false;          // will work now
                            }
                          }),
                    )
                  : state == ConnectivityState.connected
                      ? MaterialApp.router(
                         ...
                        )
                      : const Scaffold(body: Center(child: ShimmerHome()));
            }));
                    },
                  ),
      ));
    }
    
    1. declare this (var isDialogDisplayed = false;) into ur ConnectivityCubit then access it

    2. Move ur BlocConsumer into a StateFull widget then update "isDialogDisplayed" with setState({});

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search