I'm trying to stream two types of location package based data:
final _location = Location();
runApp(
MultiProvider(
providers: [
StreamProvider<PermissionStatus>(create: (_) => _location.hasPermission().asStream()),
StreamProvider<bool>(create: (_) => _location.serviceEnabled().asStream()),
],
child: MaterialApp();
)
)
When I 'stream' the data, it loads the initial value and streams that. It is not continuously listening to changes which is what I want to do. I have tried abstracting both futures into their own class and creating an async* stream that yields the current status, both of which give the same problem.
My use case involves continuously listening to the permission status and location on/off and shut down certain parts of the UI when these are modified in between tasks.
Simplified usage:
class LocationWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer2<PermissionStatus, bool>(
builder: (_, permission, isLocationEnabled, __) => _LocationWidget(
permission: permission, isLocationEnabled: isLocationEnabled));
}
}
class _LocationWidget extends StatelessWidget {
const _LocationWidget({this.permission, this.isLocationEnabled})
: assert(permission != null);
final PermissionStatus permission;
final bool isLocationEnabled;
@override
Widget build(BuildContext context) {
return Container(
child: Center(
child: (() {
if (!isLocationEnabled) // Check Bool and show different text
return Text(
"Off",
);
else
return Text("On");
}())));
}
}
ikerfah is right, you're creating a Stream from a Future, meaning the Stream will only contain a single event when the Future is completed (basically, it's not a real "stream" in the true sense of the word).
FutureBuilder won't work either, since the Future only gets completed once, so it will only trigger a single state change too.
If this is the plugin you're using, it seems the author hasn't implemented anything to expose a "real" Stream for permission change events. I wouldn't hold my breath for that either, because as far as I know neither iOS nor Android broadcast an event if/when permissions are changed.
If you need to disable/enable something based on whether permissions have changed, you'll just need to set a periodic Timer in a StatefulWidget to poll for changes.
class _LocationWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _LocationWidgetState();
}
}
class _LocationWidgetState extends State<_LocationWidget> {
PermissionStatus permission;
bool isLocationEnabled = false;
Timer _timer;
@override
void initState() {
_timer = Timer.periodic(Duration(seconds:5), (_) {
var permission = await _location.hasPermission();
var isLocationEnabled = await _location.serviceEnabled();
if(permission != this.permission || isLocationEnabled != this.isLocationEnabled)
setState(() {});
});
}
@override
void dispose() {
_timer.cancel();
}
@override
Widget build(BuildContext context) {
return Container(
child: Center(
child: (() {
if (!isLocationEnabled) // Check Bool and show different text
return Text(
"Off",
);
else
return Text("On");
}())));
}
It's up to you whether 5 seconds is an appropriate interval. initState() should probably also set the initial isLocationEnabled/permission values when the state is initialized, too, rather than waiting 5 seconds for the timer to kick in.