Issue
I have isolate that makes some heavy calculations then on receive the list with the result run a for loop to add them to observable list with items var items = [].obs;
The thing is I’m trying to observe the items list from a splash controller and once the list != [] I’ll navigate to another screen, so in onInit() I have this code:
class SplashController extends GetxController {
@override
void onInit() {
final ItemsController _itemsController = Get.put(ItemsController());
// TODO: implement onInit
super.onInit();
ever(_itemsController.items, (newItems) {
print('new items here $newItems');
});
}
}
Despite the itemsController.items is populated (after the for loop I print the itemsController.items and it’s not empty) the worker on the splash controller doesn’t trigger when the items are added.
What am I doing wrong here? Is this the correct way to observe variable outside of widget using Getx?
Can anyone help me with this, please?
Edit: In the items controller I’m adding the items this way
add(item) => items.add(item)
Solution
Continuing with the Isolate example, but without using a StatefulWidget i.e. no setState usage.
The ever
worker in SplashX will receive items generated from the Isolate. The Stateless Widget page will display the latest item emitted from the Isolate.
SplashController + ever
worker
class SplashX extends GetxController {
ItemsX itemsX;
SplashX({this.itemsX});
@override
void onInit() {
super.onInit();
ever(itemsX.items, (items) => print('Ever items: $items'));
}
}
Items Controller
class ItemsX extends GetxController {
RxList<String> items = RxList<String>();
bool running = false;
void add(String item) {
items.add(item);
}
void updateStatus(bool isRunning) {
running = isRunning;
update();
}
void reset() {
items.clear();
}
/// Only relevant for UnusedControllerPage
List<Widget> get texts => items.map((item) => Text('$item')).toList();
}
Isolate Controller
class IsolateX extends GetxController {
IsolateX({this.itemsX});
ItemsX itemsX;
Isolate _isolate;
static int _counter = 0;
ReceivePort _receivePort;
bool running = false;
static void _checkTimer(SendPort sendPort) async {
Timer.periodic(Duration(seconds: 1), (Timer t) {
_counter++;
String msg = 'notification ' + _counter.toString();
print('SEND: ' + msg);
sendPort.send(msg);
});
}
void _handleMessage(dynamic data) {
itemsX.add(data); // update observable
}
void updateStatus(bool isRunning) {
running = isRunning;
update();
}
void start() async {
itemsX.reset();
updateStatus(true);
_receivePort = ReceivePort();
_isolate = await Isolate.spawn(_checkTimer, _receivePort.sendPort);
_receivePort.listen(_handleMessage, onDone:() {
print("done!");
});
}
void stop() {
if (_isolate != null) {
updateStatus(false);
_receivePort.close();
_isolate.kill(priority: Isolate.immediate);
_isolate = null;
}
}
}
Stateless Page
class MyHomePageStateless extends StatelessWidget {
@override
Widget build(BuildContext context) {
ItemsX ix = Get.put(ItemsX()); // Instantiate ItemsController
IsolateX isox = Get.put(IsolateX(itemsX: ix));
SplashX sx = Get.put(SplashX(itemsX: ix));
return Scaffold(
appBar: AppBar(
title: Text('Isolate Stateless'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GetX<ItemsX>(
builder: (ix) => Text(ix.items.isNotEmpty ? ix.items.last : ''),
),
],
),
),
floatingActionButton: GetBuilder<IsolateX>(
builder: (_ix) => FloatingActionButton(
onPressed: _ix.running ? isox.stop : isox.start,
tooltip: _ix.running ? 'Timer stop' : 'Timer start',
child: _ix.running ? Icon(Icons.stop) : Icon(Icons.play_arrow),
),
),
);
}
}
Answered By – Baker
Answer Checked By – Cary Denson (FlutterFixes Admin)