Inside my Flutter app I am looking to inject a timer function that will execute a periodic call to an underlying service every half hour.
Currently my app is using a Provider class to hold all the business logic, including a method that calls the underlying service. This method makes a call to the service, updates the provider data properties with the results and then calls 'notifyListeners'.
To institute a periodic refresh of this process, I am thinking about using a custom stream that generates a periodic output of a value. A listener can then pick up the stream change, and then call the provider method.
My question is about how best to implement this. Should I set up the stream and listener inside the Provider class. Or should I be using a Stream builder as part of the widget hierarchy?
In the example code below, I have added a periodic streamer to simulate the timer as part of the screen setup. Is this a sound way of doing things?
#override
void didChangeDependencies() {
super.didChangeDependencies();
JourneyProvider aProvider = Provider.of<JourneyProvider>(context, listen: false);
print('initial - ' + DateTime.now().timeZoneName);
aProvider.refreshCollectors();
_timerStream = Stream<DateTime>.periodic(Duration(minutes:30,),(_) {return DateTime.now();});
_sub = _timerStream.listen((dt) {
print(dt);
aProvider.refreshCollectors();
});
}
Related
Is there a way to track if we are triggering rewind and fast forward and skipNext and skip to previous?
You can make an audio handler that wraps around your main audio handler. Your main audio handler is passed into AudioService.init, but your wrapper audio handler is used everywhere else whenever you want to manually trigger a method like fastForward. You can create the wrapper like this:
class FrontendAudioHandler extends CompositeAudioHandler {
FrontendAudioHandler(AudioHandler inner) : super(inner);
#override
Future<void> fastForward() async {
// insert any code here that should only happen when the app
// manually invokes fastForward.
await super.fastForward();
}
// Override any other methods you need to intercept.
}
Now in your app's startup, you set up two audio handlers, one wrapping the other:
final mainAudioHandler = AudioService.init(builder: () => MainAudioHandler());
final frontendAudioHandler = FrontendAudioHandler(mainAudioHandler);
When the user clicks fast forward in the notification, it will go directly to mainAudioHandler. But when you want to manually trigger a fast forward from within your Flutter code, just make sure you go through frontendAudioHandler, like this:
await frontendAudioHandler.fastForward();
This will first call the fastForward implementation above, giving you a chance to handle the manual trigger, but then that will call super.fastForward() which basically forwards on the request to your main audio handler.
This is my simple ChangeNotifier:
class Settings extends ChangeNotifier{
void changedSettings(){
notifyListeners();
}
}
I have this in my build method:
Provider.of<Settings>(context);
and the widget rebuilds as expected when I call a function that calls notifyListeners in Settings. However, I only want to run an expensive data reload if the rebuild was because of the Provider, and not because some other Flutter management reason.
My ideal solution to this is I can register some sort of callback function when I register the Provider.of in my build method...for example:
Provider.of<Settings>(context).withCallback(this._getData(this.state_settings));
Instead, what I am doing now is:
class Settings extends ChangeNotifier{
late int lastChange;
Settings(this.lastChange);
void changedSettings(){
lastChange = DateTime.now().microsecondsSinceEpoch;
notifyListeners();
}
}
...
int lastChange = Provider.of<Settings>(context).lastChange;
if(lastChange != lastSettingsChange){
lastSettingsChange = lastChange;
_getDataFuture = getData();
}
Irrespective of what triggered a rebuild, the choice to do/not do an "expensive data reload" must be managed differently. For example, you should be setting a state (that you listen to) which directs your build to do a data reload or not, based on that state.
This is how we use reactive programming and state management. The "state" must not be concerned with the "actions" taken on that state - the typical examples are the "loading", "loaded" and "error" states on performing a network read/write.
In the initState you can register a listener to this provider like Provider.of<T>(context,listen:false).addListener and this callback will be called whenever you do a notifyListener.
Or you can create your own listeners e.g look at the addStatusListener on the Animation class.
Also, if you have Provider.of<Settings>(context); in your build that means you are depending on it so the didChangeDependencies will be called when the Provider is notified.
I am new to flutter and am trying to build an app using Bloc architecture.
I could manage to figure out a sink, inputting my event bloc.updateNavigation(_selectedItem.value);. I also figured out how to use that data by calling my bloc (is that what its called). I had a TextFormField and changed some settings like this if (bloc.navigationProvider.currentNavigation == 1), if (bloc.navigationProvider.currentNavigation == 2)
There is one thing I couldn't figure out how to do though. I would like to set _Controller.clear(); whenever the bloc changes. So that whenever a user makes a new selection and changes the "currentNavigation" the text controller clears. Is there a way I can find out just when a change is made, rather than having to call by the changes. I would like to do something like if (change is made to currentNavigation) {_Controller.clear();}
My bloc file looks like this:
class NavigationBloc {
final navigationController = StreamController();
NavigationProvider navigationProvider = new NavigationProvider();
Stream get getNavigation => navigationController.stream;
void updateNavigation(int navigation) {
navigationProvider.updateNavigation(navigation);
navigationController.sink.add(navigationProvider.currentNavigation);
}
void dispose() {
navigationController.close();
}
}
final bloc = NavigationBloc();
Streams have a .listen(..) method which gets called everytime data is passed into the stream.
For better usage, use the flutter_bloc package. It provides widgets to provide a bloc to all descendants (BlocProvider) and widgets to listen to state-changes (BlocListener). There you don't have to mess with streams at all and there are a few plugins that help creating the boilerplate code.
I am learning BLoC pattern in Flutter without using any package. What I know is data are sent as Sink and output is given as Stream in BLoC pattern. StreamController is there to handle these thing. Looks like both method make changes to the input data but I am confused about their purpose. Went through the documentation but couldn't understand.
Class CartBloc{
final _cart = Cart();
Sink<Product> get addition => _additionalController.sink;
final _additionController = StreamController<Product>();
Stream<int> get itemCount => _itemCountSubject.stream;
final _itemCountSubject = BehaviorSubject<int>();
CartBloc(){
_additionaController.stream.listen(_handle);
}
void _handle(Product product){
_cart.add(product);
_itemCountSubject.add(_cart.itemCount);
}
}
Above code is from Build reactive mobile apps with Flutter (Google I/O '18). They mentioned listen is necessary because we are not just capturing data but doing some other operation through the _handle() method. Now my question is can't we implement same with stream.transform(). If yes what will be the equivalent implementation and if no what is the reason.
stream.transform(transformer) will create a new stream that is somehow modified from the original. It does not start listening on the stream, and so no values will flow through the stream until it has a listener.
Think of transform as a way to change a stream, usually for some other bit of code to listen to. Think of listen as a way to react to values that come through a stream.
I'm trying to use the Streambuilder in cooperation with Bloc. The problem is that somehow the UI updates only when the expensive Funktions are finished. It seems that then, and only then updates are made to the stream. But I can not figure out why?
I Implemented a simple Bloc that has 4 events:
1. a Future called over the Bloc Sate Object
2. a Future called inside the Bloc
3. a Funktion called inside the Bloc
4 just using Future.delay
I'm Trying to understand why everything behaves as expectetd, but not when I use first two. My guess is I have a basic misunderstanding of the eventloop but I can not see why there should be a difference in behavior between 2 and 4 or 1 and 4
To make it easy I made an example Project on Github
https://github.com/pekretsc/bloceventloop.git
So I have my refresh methode that ads the new state to the Stream.
if (event is ExpensiveEventInState) {
refresh(BlocUIState.Waiting);
String result = await blocState.doSomeThing();
if (result == '') {
refresh(BlocUIState.Fin);
} else {
refresh(BlocUIState.Fail);
}
}
if (event is ExpensiveEventWhyDoesThisWork) {
refresh(BlocUIState.Waiting);
await Future.delayed(Duration(seconds: 3));
refresh(BlocUIState.Fin);
}
so the question is, should the first and second event not behave the same way?
What happens though is that in the first case the refresh is ignored completely and just the Fin is added to the stream. (I checked that, its not that it is too fast to recognize)
StreamListener's callback is called immediately when an event is pushed on the stream.
On the other hand, StreamBuilder's builder callback isn't. For most common use-cases, it is called at most once per frame.