You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中无网络连接时如何循环显示AlertDialog?

问题描述

我需要实现以下功能:

  • 设备无网络连接时,显示提示「无网络连接,请检查网络后重试」的AlertDialog
  • 点击「Retry」按钮后,若仍无网络则持续显示该对话框;一旦网络恢复,自动关闭对话框并继续执行登录流程

我尝试在重试逻辑中使用Connectivity().checkConnectivity()和ConnectivityResult.none,但未达到预期效果。我的代码如下:

import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart';
import 'package:namer_app/Passenger_Widgets/GuestRegistration.dart';
import 'package:namer_app/login_auth/login_method.dart';
import 'package:namer_app/login_auth/resetpass_method.dart';
import 'package:namer_app/Passenger_Widgets/ScheduleWindow.dart';
import 'package:namer_app/prf_login/my_buttons.dart';
import 'package:namer_app/prf_login/my_textfields.dart';
import 'package:namer_app/prf_login/snackbar_ext.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Supabase.initialize(
    url: 'https://',
    anonKey:
        '0000',
  );

  final prefs = await SharedPreferences.getInstance();
  final bool isLoggedIn = prefs.getBool('isLoggedIn') ?? false;

  List<ConnectivityResult> connectivityResults = await (Connectivity().checkConnectivity());

  ConnectivityResult connectivityResult = connectivityResults.first;

  runApp(
    MaterialApp(
      home: MyApp(connectivityResult, prefs, isLoggedIn),
    ),
  );
}

class MyApp extends StatelessWidget {
  final ConnectivityResult connectivityResult;
  final SharedPreferences prefs;
  final bool isLoggedIn;

  MyApp(this.connectivityResult, this.prefs, this.isLoggedIn);

  @override
  Widget build(BuildContext context) {
    if (connectivityResult == ConnectivityResult.none) {
      // Show a popup to inform the user that there's no internet connection
      WidgetsBinding.instance.addPostFrameCallback((_) {
        showDialog(
          context: context,
          builder: (context) => AlertDialog(
            title: Text('No Internet Connection'),
            content: Text('Please check your internet connection and try again.'),
            actions: [
              ElevatedButton(
                child: Text('Retry'),
                onPressed: () => Navigator.pop(context),
              ),
            ],
          ),
        );
      });
    }

    return MaterialApp(
      theme: ThemeData(
        appBarTheme: AppBarTheme(
          iconTheme: IconThemeData(
            color: Colors.white,
          ), // Set back button color to white
        ),
      ),
      home: isLoggedIn ? ScheduleWindow(prefs: prefs) : LOGIN_passenger(),
    );
  }
}


class LOGIN_passenger extends StatefulWidget {
  LOGIN_passenger({super.key});

  @override
  State<LOGIN_passenger> createState() => _LOGIN_passengerState();
}

class _LOGIN_passengerState extends State<LOGIN_passenger> {
  GlobalKey<FormState> _formkey = GlobalKey();
  bool _isLoading = false;
  late TextEditingController emailController;
  late TextEditingController passController;

  //LOGIN ATTEMPTS TIMER
  int _loginAttempts = 0;
  bool _isCooldown = false;
  Timer? _cooldownTimer;
  int _cooldownTime = 60; // Cooldown time in seconds

  @override
  void initState() {
    super.initState();
    emailController = TextEditingController();
    passController = TextEditingController();
  }

  @override
  void dispose() {
    emailController.dispose();
    passController.dispose();
    _cooldownTimer?.cancel();
    super.dispose();
  }

  bool isValidEmail(String input) {
    final emailRegex = RegExp(r'^[^@]+@[^@]+\.[^@]+');
    return emailRegex.hasMatch(input);
  }

  bool isValidPhoneNumber(String input) {
    final phoneWithPrefix = '63$input';
    final phoneRegex = RegExp(r'^63\d{10}$');
    return phoneRegex.hasMatch(phoneWithPrefix);
  }

  void insertPrefs(AuthResponse response) async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    if (response.user != null) {
      final user = response.user?.id;
      final selectQuery = await Supabase.instance.client
          .from('registered_passenger')
          .select()
          .eq('user_id', user.toString());

      if (selectQuery.isNotEmpty) {
        prefs.setBool('isLoggedIn', true);
        prefs.setString('userId', selectQuery[0]['user_id']);
        prefs.setString('lastName', selectQuery[0]['last_name']);
        prefs.setString('firstName', selectQuery[0]['first_name']);
        prefs.setString('middleInitial', selectQuery[0]['middle_initial']);
        prefs.setString('email', selectQuery[0]['email']);
        prefs.setString('password', selectQuery[0]['password']);
        prefs.setString('profession', selectQuery[0]['profession']);
        String contactNumber = selectQuery[0]['phone'];
        prefs.setString('contactNumber', contactNumber.substring(2));
        prefs.setString('age', selectQuery[0]['age']);
        prefs.setString('gender', selectQuery[0]['gender']);
        prefs.setString('region', selectQuery[0]['region']);
        prefs.setString('province', selectQuery[0]['province']);
        prefs.setString('municipality', selectQuery[0]['municipality']);
        prefs.setString('barangay', selectQuery[0]['barangay']);
        prefs.setString('titlePage', 'Schedule');
        navigatetoSched(prefs);
      }
    }
  }

void _login() async {
  final connectivityResult = await (Connectivity().checkConnectivity());

  if (connectivityResult == ConnectivityResult.none) {
    context.showSnackbar(
      message: 'No internet connection. Please check your internet connection and try again.',
      backgroundColor: Colors.red,
    );
    setState(() {
      _isLoading = false;  // Reset _isLoading so the button can be clicked again
    });
    return;
  }

  if (_isCooldown) {
    context.showSnackbar(
      message: 'Too many attempts. Please wait $_cooldownTime seconds.',
      backgroundColor: Colors.red,
    );
    return;
  }

  final isValid = _formkey.currentState?.validate();

  if (isValid != null && isValid) {
    setState(() {
      _isLoading = true;
    });

    try {
      final input = emailController.text;
      final password = passController.text;
      final response;

      if (isValidEmail(input)) {
        response = await Supabase.instance.client.auth.signInWithPassword(
          email: input,
          password: password,
        );
        insertPrefs(response);
      } else if (isValidPhoneNumber(input)) {
        response = await Supabase.instance.client.auth.signInWithPassword(
          phone: '63$input',
          password: password,
        );
        insertPrefs(response);
      } else {
        throw AuthException('Invalid email or phone number');
      }

      setState(() {
        _isLoading = false;
      });
      context.showSnackbar(
        message: 'LOGIN SUCCESSFUL',
        backgroundColor: Colors.green,
      );
    } on AuthException catch (e) {
      if (e.message.contains('SocketException')) {
        context.showSnackbar(
          message: 'No internet connection. Please check your internet connection and try again.',
          backgroundColor: Colors.red,
        );
      } else {
        context.showSnackbar(
          message: e.message,
          backgroundColor: Colors.red,
        );
      }
      setState(() {
        _isLoading = false; // Reset _isLoading in case of an error
      });
    } catch (e) {
      context.showSnackbar(
        message: e.toString(),
        backgroundColor: Colors.red,
      );
      setState(() {
        _isLoading = false; // Reset _isLoading for other types of errors
      });
    }
  }
}


  
  /*Future<void> _logout() async {
  if (selectedFerry != null) {
    await _supabaseClient
        .from('boats')
        .update({'is_active': false}) // Set ferry as inactive
        .eq('boat_name', selectedFerry!)
  }

  // Continue with logout process
}*/

  void _handleLoginFailure(String errorMessage) {
    context.showSnackbar(message: errorMessage, backgroundColor: Colors.red);

    setState(() {
      _isLoading = false;
      _loginAttempts++;
    });

    if (_loginAttempts >= 3) {
      _startCooldown();
    }
  }

  void _startCooldown() {
    setState(() {
      _isCooldown = true;
    });

    _cooldownTimer = Timer.periodic(Duration(seconds: 1), (timer) {
      setState(() {
        _cooldownTime--;
      });

      if (_cooldownTime == 0) {
        timer.cancel();
        setState(() {
          _isCooldown = false;
          _loginAttempts = 0;
          _cooldownTime = 60; // Reset cooldown time
        });
      }
    });
  }

  void navigatetoSched(SharedPreferences prefs) {
    Navigator.pushAndRemoveUntil(
        context,
        MaterialPageRoute(builder: (_) => ScheduleWindow(prefs: prefs)),
        (route) => false);
  }

  void _forgotPassword() {
    Navigator.push(
      context,
      MaterialPageRoute(builder: (_) => ResetpassMethod()),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: PreferredSize(
        preferredSize: Size.fromHeight(270.0),
        child: AppBar(
          centerTitle: true,
          flexibleSpace: Container(
            decoration: BoxDecoration(
              image: DecorationImage(
                image: AssetImage('images/bgprf1.png'),
                fit: BoxFit.fill,
              ),
            ),
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Image.asset(
                    'images/prf_logo.png',
                    width: 155.0,
                    height: 155.0,
                  ),
                  SizedBox(height: 10.0),
                  Text(
                    'PASIG RIVER FERRY PORTAL',
                    style: TextStyle(
                      color: Color(0xFF33385F),
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
      backgroundColor: Colors.white,
      body: Container(
        padding: EdgeInsets.all(20.0),
        child: SingleChildScrollView(
          child: Form(
            key: _formkey, // Associate the Form with the GlobalKey
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                SizedBox(height: 30.0),
                Text(
                  'WELCOME',
                  style: TextStyle(
                    color: Color(0xFF33385F),
                    fontSize: 30,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                Text(
                  'Please, Login to your account!',
                  style: TextStyle(
                    color: Color(0xFF666666),
                    fontSize: 15,
                    fontWeight: FontWeight.bold,
                  ),
                ),

                const SizedBox(height: 20.0),
                // Textfield for Email/Username
                Textfield_auth(
                  controller: emailController,
                  prefixIcon: Icon(Icons.person),
                  hintText: 'Enter Email/Username',
                  labelText: 'Email/Username',
                  obscureText: false,
                ),

                const SizedBox(height: 15.0),
                // Textfield for Password
                Textfield_authpass(
                  controller: passController,
                  prefixIcon: Icon(Icons.lock),
                  hintText: 'Enter Password',
                  labelText: 'Password',
                ),

                Row(
                  mainAxisAlignment: MainAxisAlignment.end,
                  children: [
                    GestureDetector(
                      onTap: _forgotPassword,
                      child: Text(
                        'Forgot Password?',
                        style: TextStyle(
                          color: Color(0xFF666666),
                          fontSize: 12,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ),
                  ],
                ),
                SizedBox(height: 30.0),

                LoginButton(
                  onTap: _isLoading || _isCooldown ? null : _login,
                ),
                if (_isCooldown)
                  Center(
                    child: Text(
                      'Please wait $_cooldownTime seconds to try again.',
                      style: TextStyle(
                        color: Colors.red,
                        fontSize: 16,
                      ),
                    ),
                  ),

                const SizedBox(height: 20.0),
                // Continue with Google account
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 5.0),
                  child: Row(
                    children: [
                      Expanded(
                        child: Divider(
                          thickness: 1,
                          color: Colors.grey,
                        ),
                      ),
                      Padding(
                        padding: const EdgeInsets.symmetric(horizontal: 10.0),
                        child: Text(
                          'OR',
                          style: TextStyle(color: Colors.grey),
                        ),
                      ),
                      Expanded(
                        child: Divider(
                          thickness: 1,
                          color: Colors.grey,
                        ),
                      ),
                    ],
                  ),
                ),

                SizedBox(height: 30.0),
                // GOOGLE SIGN IN
                Center(
                  child: Row(
                    mainAxisSize: MainAxisSize.max,
                    children: [
                      const SizedBox(height: 5),
                      GuestButton(
                        onTap: () {
                          
                          Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (context) => PrfGuestRegistration(),
                            ),
                          );
                        },
                      ),
                      SignUpButton(
                        onTap: () {
                          Navigator.push(
                            context,
                            MaterialPageRoute(
                              builder: (context) => ChooseLoginMethod(),
                            ),
                          );
                        },
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
解决方案

核心问题是你当前仅做了单次网络状态检查,没有持续监听网络变化,且点击Retry时直接关闭弹窗,未验证网络是否恢复。以下是针对性修改:

1. 将MyApp改为StatefulWidget,实现网络状态监听

把原来的Stateless改为Stateful,通过Connectivity的流实时监听网络变化,自动控制弹窗的显示和关闭:

class MyApp extends StatefulWidget {
  final SharedPreferences prefs;
  final bool isLoggedIn;

  MyApp(this.prefs, this.isLoggedIn);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late StreamSubscription<List<ConnectivityResult>> _connectivitySubscription;
  bool _isNoNetwork = false;

  @override
  void initState() {
    super.initState();
    // 初始化检查网络状态
    _checkInitialConnectivity();
    // 监听网络状态变化
    _connectivitySubscription = Connectivity().onConnectivityChanged.listen((results) {
      final hasNetwork = results.any((result) => result != ConnectivityResult.none);
      setState(() {
        _isNoNetwork = !hasNetwork;
      });
      if (hasNetwork && _isNoNetwork) {
        // 网络恢复,关闭弹窗
        Navigator.pop(context);
      } else if (!hasNetwork) {
        // 网络断开,显示弹窗
        _showNoNetworkDialog();
      }
    });
  }

  Future<void> _checkInitialConnectivity() async {
    final results = await Connectivity().checkConnectivity();
    setState(() {
      _isNoNetwork = !results.any((result) => result != ConnectivityResult.none);
    });
    if (_isNoNetwork) {
      WidgetsBinding.instance.addPostFrameCallback((_) => _showNoNetworkDialog());
    }
  }

  void _showNoNetworkDialog() {
    // 避免重复弹出相同弹窗
    if (ModalRoute.of(context)?.isCurrent == false) return;
    showDialog(
      context: context,
      barrierDismissible: false, // 禁止点击外部关闭弹窗
      builder: (context) =>
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 11:53:54