I am working on a Flutter app and I need to send a stream of data from my DatabaseHandler class to a stateful widget, but the listener isn't working (print statements confirm stream is being initialized and updated, print statement in listener method doesn't get called).
I've created an example version of how I have it set up below:
import 'package:flutter/material.dart';
import 'dart:async';
Future<void> main() async {
runApp(const MyApp());
Timer.periodic(
const Duration(seconds: 4),
(timer) {DatabaseHandler().startStream();
}
);
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState(){
DatabaseHandler().testStream.listen((int i) {
print('Received location marker $i');
});
super.initState();
}
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: Text('Hello World!'),
),
),
);
}
}
class DatabaseHandler{
late StreamController<int> _testStreamController;
Stream<int> get testStream => _testStreamController.stream;
DatabaseHandler() {
_testStreamController = StreamController<int>();
print('Stream initialized');
}
void startStream() async {
for (int i = 1; i <= 5; i++) {
_testStreamController.add(i);
print('Test stream: $testStream');
}
}
}
The issue here is that you're creating a new instance of DatabaseHandler in both main() and initState(). This means that the StreamController you're listening to in initState() is not the same one you're adding data to in main().
To fix this, you should create a single instance of DatabaseHandler and use it in both places. Here's how you can do it:
import 'package:flutter/material.dart';
import 'dart:async';
// Create a single instance of DatabaseHandler
final databaseHandler = DatabaseHandler();
Future<void> main() async {
runApp(const MyApp());
Timer.periodic(
const Duration(seconds: 4),
(timer) {databaseHandler.startStream();
}
);
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
void initState(){
// Use the same instance of DatabaseHandler
databaseHandler.testStream.listen((int i) {
print('Received location marker $i');
});
super.initState();
}
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: Text('Hello World!'),
),
),
);
}
}
class DatabaseHandler{
late StreamController<int> _testStreamController;
Stream<int> get testStream => _testStreamController.stream;
DatabaseHandler() {
_testStreamController = StreamController<int>();
print('Stream initialized');
}
void startStream() async {
for (int i = 1; i <= 5; i++) {
_testStreamController.add(i);
print('Test stream: $testStream');
}
}
}
Now, the StreamController you're listening to in initState() is the same one you're adding data to in main(), so you should see the print statements from the listener.