تكرار السؤال: …
الإجابة المختصرة
استخدم حالات واضحة مثل connecting وconnected وdisconnected وerror بدل flags متفرقة.
الشرح
تتفرع حالات الاتصال بين connecting و connected و reconnecting و disconnected و failed، وتؤثر في الواجهة (شريط "لا يوجد اتصال")، وفي المنطق (هل نضع الرسائل في طابور أم نرفضها؟)، وفي إعادة المحاولة. تعدد الأعلام المنفصلة (isConnecting و isConnected و hasError) يسمح بتركيبات متناقضة مثل أن يكون الاتصال متصلًا وفاشلًا معًا. الحل نوع مغلق (sealed class) أو enum يمثل حالة واحدة فقط في كل لحظة، وتعرضه الخدمة كـ Stream أو ValueListenable تستهلكه الواجهة، فتسهل اختبار الانتقالات (connecting → connected → reconnecting) دون شبكة حقيقية.
مثال بالكود
sealed class ConnectionState {
const ConnectionState();
}
class Connecting extends ConnectionState { const Connecting(); }
class Connected extends ConnectionState { const Connected(); }
class Reconnecting extends ConnectionState {
const Reconnecting(this.attempt);
final int attempt;
}
class Disconnected extends ConnectionState {
const Disconnected(this.reason);
final String reason;
}
class SocketService {
final state = ValueNotifier<ConnectionState>(const Disconnected('not started'));
// transitions are set in one place: state.value = const Connecting(); ... = const Connected();
}
// UI
ValueListenableBuilder<ConnectionState>(
valueListenable: service.state,
builder: (_, state, __) => switch (state) {
Connected() => const SizedBox.shrink(),
Connecting() || Reconnecting() => const LinearProgressIndicator(),
Disconnected(:final reason) => Text('Offline: $reason'),
},
);أخطاء شائعة
- اعتبار الاتصال "متصلًا" فور استدعاء connect قبل اكتمال المصافحة فعليًا.
- ترك الحالة القديمة بعد إغلاق الاتصال فتعرض الواجهة "متصل" وهي مقطوعة.
- استخدام عدة أعلام boolean متفرقة تسمح بتركيبات مستحيلة بدل حالة واحدة.