How to make a flutter app react to network changes? - flutter

I have this method, in one of my classes, that registers a listener to watch for connectivity changes:
static _watchNetworkChanges() {
Connectivity().onConnectivityChanged.listen((status){
if (status == ConnectivityResult.wifi) {
// Do something...
}
});
}
But apparently, the closure passed to the listen method is never called, no matter what I try (enable/disable wi-fi, mobile network, etc.)
The Connectivity class is part of this lib:
http://47.75.70.92/documentation/connectivity/0.4.1/
Does anyone know how to make this work?
EDIT:
I found out that this listener works if it is written inside an initState() method of a StatefulWidget. But that doesn't help me, because I'm writing a library and therefore I will have no widgets inside it.

Related

Exactly why and when are StreamController constructor functions onListen, onPause, onResume, and onCancel called?

In Dart/Flutter, I have a StreamController defined like this:
MyEventStreamer() {
_controller = StreamController<TimedEvent>(
onListen: _startStream,
onResume: () {
throw UnimplementedError();
},
onPause: () {
throw UnimplementedError();
},
onCancel: _stopStream);
_calculateEventTimes();
}
The reason I have the throws is that I don't want onPause or onResume to be used... and I want to throw an error in case I forget this later.
However, since these are apparently not called explicitly (and the docs don't seem to describe when they are called), I'm concerned that these may be called by the system under certain unknown circumstances... such as lifecycle events on mobile when the app is backgrounded, etc... which would be bad.
Is this a legitimate concern -- or are these methods never called by anything other than code the programmer wrote?
OnListen - When there is a listener attached.
OnCancel - When listener cancels listening to the events.
There are few scenarios where pause/resume is called.
If you create a stream but don't want any event to emit until there is at least one listener. This way, all the events prior to the first listener is buffered and sent to the attached listener. Here you create a stream, pause it and call it's resume once there is onListen called.
While using asyncExpand/asyncMap where each event is transformed into another event as per user's provided convert method. Each event is passed to asyncExpand/asyncMap to get new event which could take some time (in rare case). While these converter methods haven't returned their values, stream should not emit more values and it should buffer new events.
Maybe you are connected to a websocket or firebase and you want to not listen to any events when app is in background, you can call pause/resume to defer data once app is in foreground.
There could be other use cases but these are the use-cases provided in flutter framework I am aware of.

Synchronous platform code execution in Flutter

I am calling a native SDK which goes off and does some API calls, which is obviously time-bound work. For 90% of this work I am using callbacks and method channel calls going bi-directional so Dart can call Swift, and Swift can then call Dart when finished.
The problem I have is when I want to synchronously await the API response (or timeout).
I ran some tests, as I originally was going to just hold off sending back the FlutterResult until the Swift callback was hit, but this severely slows down the UI, as I believe all platform calls are made on the UI thread. I simulated a 10 second response time and the app would completely hang for a good few seconds before becoming responsive again, and if I spammed a few of these requests the app would hang for 10 seconds plus.
I am a bit confused on what the best way to run synchronous platform code is, because I don't think platform code can be called from an isolate.
I want to
Get user details
User presses button
Show animation whilst waiting
Get response and move on.
The SHOW ANIMATION WHILE WAITING is the part that of course, janks. I'm not entirely sure why either because running general CPU work whether its native or dart shouldn't freeze the rendering engine surely?
Anyway, the only thing I can think of is:
Dart calls platform method A
Set flag AwaitingMethodA to true
Immediately return FlutterResult but set a callback in Swift
while (AwaitingMethodA)....
When callback in Swift hits, jump on the UI thread and send message back to Dart saying complete, which sets AwaitingMethodA to false
Dart code continues....
^ and I could set a timeout timer for the above to break out the loop if necessary.
Is there anything wrong with this approach, or is there a better convention for synchronous, time-bound platform work?
Don't wait in while loop - this is the problem.
You should use Completer class (https://api.flutter.dev/flutter/dart-async/Completer-class.html). As the documentation says - with Completer you can easily convert callback based api to future based one.
Your code could look like this:
class SdkResultType {
//let's assume this is the type that Sdk will return
}
class MyClass {
Completer _completer;
Future<SdkResultType> callSdk() {
_completer=Completer();
///this is where you would call the SDK
somehowCallTheSDK();
// this is where your code will wait
return _completer.future;
}
// your SDK callback - SDK should call this when completed
void sdkCallback(SdkResultType sdkResult) {
if (!_completer.isCompleted) {
//this is where you complete the _completer.future, unblocking your mainSdkCall function.
_completer.complete(sdkResult);
}
}
void mainSdkCall() async {
//this is whare you wait
var sdkResult=await callSdk();
// do the rest here
}
}

Provider - Selector rebuild when scrolling

I am using the Selector widget as shown below
Nothing wrong with it's build method, only call it when the value changes.
But when I use Devtools or android studio to track widget rebuild it's showing that the Selector it self rebuild when I am scrolling whether in a list or any other widget that support scrolling.
Yes the Selector didn't call the build method until the value changes but is this normal ?
Using Devtools:
As you can see the others (2) Selectors doesn't have to be triggers but yet they are.
sorry for my bad English, I can explain in another way in the comment section if you didn't understand me and thanks in advance.
edit:
I guess I know why the selector is rebuilding it's self, because I am using the provider class as listener to scroll controller direction with changenotifier.
here the code
in provider class:
bool isHideHomeScreenTabBar = false;
void hideShowTabBar(ScrollDirection scrollDirection) {
isHideHomeScreenTabBar = scrollDirection == ScrollDirection.reverse;
notifyListeners();
}
in my Home screen:
_scrollController.addListener(() {
Provider.of<AppProvider>(context, listen: false).hideShowTabBar(
_scrollController.position.userScrollDirection);
});
So basically the provider trigger changenotifier with every scroll I do and the selector get notified and rebuild it's self but if the value didn't change the selector won't trigger the build method (so it works fine for the child and the widget in the build method of the selector).
But even so is this normal ? and why, The other selectors aren't even listening to the scroll direction.
Anyway I found an alternative way to do this (with an animation controller) but it would be nice if someone could explain what is happening here, it's important for me at least because I might use another state management.
I know what was happing.
I am using 1 class for the provider that contains all the values I need with many methods using notifyListeners, however I thought it's ok to use 1 provider class if I use Selector for every value I had so anything that need rebuild will only rebuild when it's need it.
The problem with this approach is that with every notifyListeners call every selector got notified and rebuild it self (in my case when any scrolling detected) but the selector doesn't call the builder if the value not changed.
The fix is to make some condition that check the old value and the new value before calling notifyListeners, that works prefect in my case, this decrease the rebuilding happing when I scroll to only 1 as it's intended to be, however the other selectors in the same class also rebuild (I guess because they are all in the same class so every notifyListeners call effect them).
At the end if you end up with similar issue it is better to use ProxyProvider or any way that let you use multiple providres, beside the benefit of a better project architecture and disposing it is way better to have more control over the state.
Thanks to RĂ©mi Rousselet Riverpod it's way better than ProxyProvider and I am using it and it's awesome so consider Riverpod if you want to use ProxyProvider.

Flutter: How to track app lifecycle without a widget

I'm involved with the FlutterSound project which is shipped as a package containing an api that, for the purposes of this question, doesn't contain a widget.
The api needs to handle events when the application changes its state (AppLifecycleState.pause/resume). (we need to stop/resume audio when the app is paused/resumed).
I can see how to do this in a widget using WidgetsBindingObserver but the api needs this same info without having to rely on a widget.
The SchedulerBinding class has a method handleAppLifecycleStateChanged which seems to provide the required info but its unclear how to implement this outside of a widget.
Below is a code sample that can listen to AppLifecycleState change events without directly involving a widget:
import 'package:flutter/material.dart';
class MyLibrary with WidgetsBindingObserver {
AppLifecycleState _state;
AppLifecycleState get state => _state;
MyLibrary() {
WidgetsBinding.instance.addObserver(this);
}
/// make sure the clients of this library invoke the dispose method
/// so that the observer can be unregistered
void dispose() {
WidgetsBinding.instance.removeObserver(this);
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
this._state = state;
}
void someFunctionality() {
// your library feature
}
}
Now you can instantiate the library in a flutter widget, for instance, from which point it will start listening to any change in AppLifecycleState.
Please note that, the above code doesn't take care of redundant bindings. For example, if the client of your library is meant to initialize the library more than once in different places, the didChangeAppLifecycleState() method will be triggered multiple times per state change (depending on the number of instances of the library that were created). Also I'm unsure whether the solution proposed conforms to flutter best practices. Nevertheless it solves the problem and hope it helps!

Why do we use the dispose() method in Flutter Dart code?

Why are we using dispose() method? I'm little confused about it.
what will be issue occurs If we don't use it and what's the benefit of using it?
#override
void dispose() {
// TODO: implement dispose
super.dispose();
}
dispose method used to release the memory allocated to variables when state object is removed.
For example, if you are using a stream in your application then you have to release memory allocated to the stream controller. Otherwise, your app may get a warning from the PlayStore and AppStore about memory leakage.
dispose() method called automatically from stateful if not defined.
In some cases dispose is required for example in CameraPreview, Timer etc.. you have to close the stream.
When closing the stream is required you have to use it in dispose method.
dispose() is used to execute code when the screen is disposed. Equal to onDestroy() of Android.
Example:
#override
void dispose() {
cameraController?.dispose();
timer.cancel();
super.dispose();
}
The main purpose is to get a callback where in you can free-up all your resources.
If you have initialized any resource in a State, it is important that you close or destroy that resource when that state is disposed.
For e.g: If you are creating a stream in initState of your StatefullWidget, then it is important that you close that stream in dispose method of that state or else it will cause memory leak.
For more details you can refer following comments which I got from the source code of the dispose method of the StatefulWidget:
Called when this object is removed from the tree permanently. The
framework calls this method when this [State] object will never build
again. After the framework calls [dispose], the [State] object is
considered unmounted and the [mounted] property is false. It is an
error to call [setState] at this point. This stage of the lifecycle is
terminal: there is no way to remount a [State] object that has been
disposed. Subclasses should override this method to release any
resources retained by this object (e.g., stop any active animations).
{#macro flutter.widgets.subscriptions} If you override this, make sure
to end your method with a call to super.dispose(). See also: *
[deactivate], which is called prior to [dispose].
Or you can refer the docs: https://api.flutter.dev/flutter/widgets/State/dispose.html
So basically dispose is called when that current state will never be used again. So, if you are having any listeners that are active in that state then they can cause memory leaks and so you should close them.
dispose() method is called when this object is removed from the tree permanently.
For more information, you can refer official Docs: https://api.flutter.dev/flutter/widgets/State/dispose.html
Implementation Example:
#protected
#mustCallSuper
void dispose() {
assert(_debugLifecycleState == _StateLifecycle.ready);
assert(() {
_debugLifecycleState = _StateLifecycle.defunct;
return true;
}());
}
In general terms dispose means freeing the resources before the related object is removed from the focus. By focus, I mean from widget tree or navigation stack whichever is relevant.
When you call dispose on a widget state, the associated widget is supposed to be unmounted which means the widget will never rebuild.
Called when this object is removed from the tree permanently.
The framework calls this method when this State object will never build again.
After the framework calls dispose, the State object is considered unmounted and the mounted
property is false. It is an error to call setState at this point.
This stage of the lifecycle is terminal: there is no way to remount a State object that has been
disposed.
When you call dispose on a bloc, the bloc is supposed to be closing the event and state stream.
Update: New Bloc does not have dispose, instead it has close.
So, This is what dispose basically means.
TL;DR
In general, it means releasing the resources held by the associated instance/object.
The specific meaning of dispose, however, changes with the types of the object on which this method is called.
Well, the answer is in the word dispose of. so imagine you are at a party and there is a table where glasses of water are placed which are plastic glasses. Now you will get one glass use it and dispose (throw it into the dustbin). if you don't do that you place the same glass on the table then there will be no place for other new glasses to be put(memory error). Because the table is full now. The second thing is if you put a glass on the table it's possible there might some drink is left. so if someone else uses that glass then there will be something in it already. (controller already having some value if you don't dispose and the same form or animation controller on the same screen).
happy scene:- all take glasses of drink and dispose them into dustbin so all table will also get space and everybody will get new drink not the old drink of someone.
Practical where I got to know the accurate use- I made a screen where inline editing was needed means you click the button all text converts into text form fields and you change the required values and press that button again to submit(icon change of the same button on editing),
So the word dispose expose everything about its use I hope this real-life example will help a little bit. Thank you
void dispose() {
super.dispose();
_editButtonAnimationController.dispose();
_ageController.dispose();
}
You should not only override dispose method to free up some memory but also to dispose those objects that would otherwise be visible on the screen like a BannerAd.
Say, you have a BannerAd and you don't dispose the instance of bannerAd and navigate back to the previous page, your ad would still be visible on the screen which you don't want. So, you must dispose it like:
#override
void dispose() {
bannerAd?.dispose();
super.dispose();
}
Given all the answers above regarding garbage collection and examples of what to put in dispose(), it seems like the general idea is to dispose/cancel anything that has a future or is tied to hardware access, because you don't want to return to a non-existent object or to block other apps from using that resource (ex.camera, file system, microphone, etc.).
We uses dispose() method in order to stop/avoid memory linkage when state object is removed from the widget tree.