الخرائط والموقع وميزات الجهاز

مراقبة اتصال الشبكة

16 دقيقة الدرس 11 من 12

مراقبة اتصال الشبكة

يجب على التطبيقات المحمولة الحديثة التعامل بسلاسة مع ظروف الشبكة غير الموثوقة. بدلاً من السماح لطلبات الشبكة بالفشل بصمت، يمكن لتطبيقات Flutter استخدام حزمة connectivity_plus للتحقق من حالة الشبكة الحالية عند التشغيل والاستماع إلى التغييرات في الوقت الفعلي — مع عرض تغذية راجعة مناسبة داخل التطبيق عندما يتعذر الاتصال أو يعود.

إضافة حزمة connectivity_plus

أضف الحزمة إلى ملف pubspec.yaml:

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  connectivity_plus: ^6.0.3

شغّل الأمر flutter pub get لتنزيل التبعية. على Android، لا تلزم أذونات إضافية لقراءة حالة الاتصال. وعلى iOS، تعمل الحزمة مباشرةً دون أي تعديلات على Info.plist.

واجهة برمجة التطبيقات الرئيسية

تُعرض الحزمة من خلال صنف وحيد هو Connectivity مع نقطتَي دخول رئيسيتين:

  • checkConnectivity() — تُعيد Future<List<ConnectivityResult>> يحتوي على نوع (أو أنواع) الاتصال الحالية.
  • onConnectivityChangedStream<List<ConnectivityResult>> يُصدر حدثاً في كل مرة تتغير فيها حالة الشبكة.

يشمل التعداد ConnectivityResult قيماً مثل wifi وmobile وethernet وvpn وbluetooth وnone. قد يُبلّغ الجهاز عن نتائج متعددة في آنٍ واحد (مثلاً wifi وvpn معاً).

ملاحظة: تُبلّغ connectivity_plus عن نوع واجهة الشبكة، لا عمّا إذا كانت الإنترنت متاحة فعلياً. جهاز متصل بشبكة WiFi لا تملك اتصالاً صاعداً سيُعيد مع ذلك ConnectivityResult.wifi. للتحقق الحقيقي من إمكانية الوصول، اجمع هذه الحزمة مع فحص HTTP أو استخدم internet_connection_checker_plus.

فحص الاتصال عند التشغيل

أنظف نمط هو إجراء الفحص الأولي داخل initState ثم الاشتراك فوراً في الدفق:

NetworkStatusService — الفحص والاستماع

import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart';

class NetworkAwarePage extends StatefulWidget {
  const NetworkAwarePage({super.key});

  @override
  State<NetworkAwarePage> createState() => _NetworkAwarePageState();
}

class _NetworkAwarePageState extends State<NetworkAwarePage> {
  final Connectivity _connectivity = Connectivity();
  late StreamSubscription<List<ConnectivityResult>> _subscription;

  bool _isOnline = true;
  ConnectivityResult _connectionType = ConnectivityResult.none;

  @override
  void initState() {
    super.initState();
    _checkInitialConnectivity();
    _subscription = _connectivity.onConnectivityChanged.listen(_onConnectivityChanged);
  }

  Future<void> _checkInitialConnectivity() async {
    final results = await _connectivity.checkConnectivity();
    _onConnectivityChanged(results);
  }

  void _onConnectivityChanged(List<ConnectivityResult> results) {
    final hasConnection = results.any(
      (r) => r != ConnectivityResult.none,
    );
    setState(() {
      _isOnline = hasConnection;
      _connectionType = results.isNotEmpty ? results.first : ConnectivityResult.none;
    });
  }

  @override
  void dispose() {
    _subscription.cancel();  // ألغِ دائماً لمنع تسرب الذاكرة
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('عرض الشبكة')),
      body: Column(
        children: [
          if (!_isOnline)
            Container(
              width: double.infinity,
              color: Colors.red.shade700,
              padding: const EdgeInsets.symmetric(vertical: 8),
              child: const Text(
                'لا يوجد اتصال بالإنترنت',
                textAlign: TextAlign.center,
                style: TextStyle(color: Colors.white),
              ),
            ),
          Expanded(
            child: Center(
              child: Text(
                _isOnline
                    ? 'متصل عبر $_connectionType'
                    : 'غير متصل',
                style: const TextStyle(fontSize: 18),
              ),
            ),
          ),
        ],
      ),
    );
  }
}
نصيحة: ألغِ دائماً StreamSubscription داخل dispose(). الإخفاق في ذلك يُبقي الاشتراك حياً حتى بعد إزالة الودجت من الشجرة، مما يُسبب تسرب الذاكرة وأخطاء setState على ودجت منتهي الصلاحية.

استخراج ConnectivityService قابل لإعادة الاستخدام

في التطبيقات الأكبر حجماً، عزل منطق الاتصال في صنف خدمة مخصص وحقنه في الودجات عبر حل لإدارة الحالة مثل Provider أو Riverpod. يجعل ذلك الخدمة قابلة للاختبار ويتجنب تكرار نسخة Connectivity في التطبيق.

ConnectivityService — نمط أحادي قابل لإعادة الاستخدام

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

class ConnectivityService {
  final Connectivity _connectivity = Connectivity();

  // دفق بث يمكن للودجات الاستماع إليه
  Stream<bool> get onStatusChange => _connectivity.onConnectivityChanged.map(
        (results) => results.any((r) => r != ConnectivityResult.none),
      );

  /// تُعيد true إذا كانت أي واجهة غير none متاحة الآن.
  Future<bool> get isConnected async {
    final results = await _connectivity.checkConnectivity();
    return results.any((r) => r != ConnectivityResult.none);
  }

  /// تسمية مقروءة بشرياً لنوع الاتصال الأساسي.
  Future<String> get connectionLabel async {
    final results = await _connectivity.checkConnectivity();
    if (results.contains(ConnectivityResult.wifi)) return 'WiFi';
    if (results.contains(ConnectivityResult.mobile)) return 'بيانات الهاتف';
    if (results.contains(ConnectivityResult.ethernet)) return 'إيثرنت';
    return 'غير متصل';
  }
}

عرض التغذية الراجعة داخل التطبيق

تشمل الأنماط الشائعة لإظهار تغييرات الاتصال للمستخدم:

  • شريط بانر — شريط ملون ثابت في أعلى الشاشة أو أسفلها (كما هو موضح أعلاه).
  • SnackBar — إشعار مؤقت يختفي تلقائياً، مناسب لأحداث إعادة الاتصال.
  • تراكب / حجب الشاشة الكاملة — للتطبيقات التي لا تدعم الاستخدام دون اتصال.
  • تعطيل الإجراءات — تعتيم أزرار الرفع والإرسال عند انقطاع الاتصال.
تحذير: تجنب عرض SnackBar عند كل تغيير في الاتصال إذا كان الدفق يُطلق بسرعة (مثلاً عند التبديل بين WiFi والبيانات المحمولة). استخدم تقنية التأخير (debounce) أو التخفيف (throttle)، أو أخطر المستخدم فقط عندما تختلف الحالة النهائية عن السابقة، لمنع سيل من الإشعارات.

ملخص

توفر حزمة connectivity_plus أداتين متكاملتين: مستقبل checkConnectivity() للمرة الواحدة لفحص التشغيل، ودفق onConnectivityChanged للتحديثات التفاعلية. النمط المعياري هو: (1) إضافة الحزمة، (2) استدعاء المستقبل في initState، (3) الاشتراك في الدفق وتحديث الحالة، و(4) إلغاء الاشتراك في dispose(). في تطبيقات الإنتاج، اقرن ذلك بفحص فعلي لإمكانية الوصول إلى الإنترنت واستخرج المنطق في خدمة مشتركة.