تكرار السؤال: …
الإجابة المختصرة
احتفظ بالـsubscription وألغِه في dispose، ولا تشترك داخل build.
الشرح
الاشتراك في Stream أو WebSocket يبقي المستمع (listener) حيًا ما لم يُلغَ صراحة. إذا غادر المستخدم الشاشة ولم يُلغَ الاشتراك فسيستمر استدعاء setState على State منتهية (استثناء "setState called after dispose")، ويبقى مرجع إلى الـ Widget يمنع جمع الذاكرة، وقد يتراكم عدد الاشتراكات المكررة عند كل دخول للشاشة. احتفظ بكائن الـ StreamSubscription في الـ State، واشترك في initState (لا في build)، وألغِه في dispose، وأغلق القناة التي أنشأتها الشاشة. وعندما يملك الاتصال طبقة أعلى (خدمة مشتركة) فلتلغِ كل شاشة اشتراكها هي فقط.
مثال بالكود
class LivePricesPage extends StatefulWidget {
const LivePricesPage({super.key});
@override
State<LivePricesPage> createState() => _LivePricesPageState();
}
class _LivePricesPageState extends State<LivePricesPage> {
late final StreamSubscription<Price> _subscription;
Price? _latest;
@override
void initState() {
super.initState();
_subscription = priceService.stream.listen((price) {
if (!mounted) return;
setState(() => _latest = price);
});
}
@override
void dispose() {
_subscription.cancel(); // stop receiving events and release the reference to this State
super.dispose();
}
@override
Widget build(BuildContext context) => Text('${_latest?.value ?? '--'}');
}أخطاء شائعة
- الاشتراك داخل build فيتكرر الاشتراك مع كل إعادة بناء.
- نسيان cancel عند تبديل الحساب أو الخروج من الشاشة.
- استدعاء setState بعد await دون التحقق من mounted.