The more things I do, the more often I run across things that need disposing (e. g. Timers and ScrollControllers). Two questions:
Is there a way to see/know what things need to be disposed? Or is it just a thing you need to learn by heart? For example: At the moment I'm not sure, if I need to dispose of providers.
Do I need to expose things (e. g. Timers) in stateless widgets? If so how? Or do I turn a widget stateful just so I gain access to the dispose method? That seems wasteful.
Thanks!
If disposing is needed, it's usually stated in docs.
It's not about turning into a stateful widget "just to gain access to the dispose method". It's turning into a stateful widget because it has a state. If it contains a timer, it already implies having a state. Think of stateless widgets as pure functions – you can call them several times and there should be no side effects. Timer is such a side effect since you don't want it to be re-created on every stateless widget creating. Also, there's no significant performance impact on converting stateless widget to stateful.
Related
I noticed the more you got variables in your statefulWidget, the more the setState function takes time to complete.
Making the app kinda slow, and this is quite annoying.
Is there a way to change state for Only one variable, please? I mean something like this:
setState(...varToUpdate)
No. setState by design is rebuilding all widgets that depend on the state on which the method os being called.
If you refactor your monolithic widget into sub-widgets, you can have finer-grain control over what gets rebuilt. Also, you should look into a state management solution like RiverPod to be able to narrow down "consumers" to be associated with their triggers, which helps tremendously.
Also, if your build is expensive, you are doing something wrong. A build should be cheap, capable of being performed 60 times per second with no I/O or expensive calculations.
No there isn't, since setState rebuilds the whole widget.
It updates all variables.
Performance is just slow in debug mode. Once you build your app in release mode it should be faster.
I have a widget, that implements an audioplayers library and provide button to play/pause audio file and also displays current position and duration of audio file. Second widget that i have is a Slider with a lot of customization so it was defined as a separate widget. All i need to do, is somehow share position and duration of AppAudioPlayer widget between itself and Slider and also provide access for Slider to audioplayers AudioPlayer class seek() method to seek the position by changing the slider value. I've tried to implement Provider, and GlobalKey, but these task is a bit complicated for this approaches. Also thought about Stream to share position and duration, but doesn't understand how to share instance of AudioPlayer. Would love to consider all suggestions!
So you are in need of state management and need to share state between two widgets.
First i would read Flutters overview on State managment. I would also consider RiverPod, a new and improved state management solution by the creator of Provider. Or Binder, a simpler solution, although, not actively maintained ATM.
Although, i think in your case, i would look into using ValueNotifier to keep a single state and then ValueListenablebuilder to listen for state changes. I find this a very simple and pragmatic solution for most cases involving state, and also part of the core Flutter library.
You haven't posted any code hence I have no idea how your code looks like.
If you want to share data between 2 widgets then you can create a global variable and use that variable in both the widgets. Then change the variable accordingly. Now this might not be a solution to your problem but I don't have your code so...
Also thought about Stream to share position and duration, but doesn't understand how to share instance of AudioPlayer
I don't think that's how streams work. Streams are not used for passing data inside your code. You can read about streams here
I have an architectural question.
First of all, I understand that Navigator 2.0 enables updating the URL path of the app. For Flutter web applications and for deep linking on iOS and Android, this can obviously be very important in certain situations. However, most applications probably don't need the amount of complexity involved in implementing URL sync with Navigator 2.0.
So, that brings us back to navigation with Navigator 1.0. The issue I have with the approach they require is that implementation is like the opposite of what you'd achieve from dependency injection in terms of how it affects life cycle management and control flow. By forcing construction of objects (in this case widgets) to occur deep in your code, it sort of convolutes your architecture... For example, instead of having an easy to follow hierarchy, you now have much greater cyclomatic complexity as you also need to worry about managing navigation state and controlling application flow from a second dimension (instead of just leveraging the hierarchy.) To me, it seems like it would violate SOLID to allow a deeply nested child to change which extended relatives are displayed because it seems like that relative's rendering behavior should be controlled by its parent, not some random other widget in some unknown part of the hierarchy. As an analogy, that would be like my child calling their second cousin to take them out of school instead of that decision (for the cousin to go to school or not) being made by the cousin's parent.
In contrast, by leveraging stateful widgets, screens and child widgets can be conditionally displayed without relying on Navigation. This approach makes the hierarchy clean and easy to read and follow. If you need to troubleshoot the display of a widget, you only need to walk up the hierarchy (and occasionally back down to a sibling, depending on how you're propagating state.) By using the Provider architecture (like StreamProvider and ChangeNotifier), state and changes can be gracefully propagated throughout the application. So, a child deep in the hierarchy could use a model class to propagate a state change to an observer that conditionally displays child widgets in a reactive manner. (Going back to the analogy, that would be like my child broadcasting that there was a family emergency, and their second cousin's parent reacts to that by pulling their child out of school that day.)
This reactive design allows cleaner separation of concerns because a descendent never needs to worry about how some extended relative wants to react to a state change (such as by displaying a different screen.) It also makes debugging and testing a lot simpler.
With all that said, it does appear that Navigation is required to get proper behavior of the back button on a device. Is that the only redeeming value of using Navigation? Can someone please help me understand if I'm missing something?
Also, if that's the main purpose of using Navigation, is there a principle I can follow to use it without turning my control flow into a spaghetti mess?
I'm totally new to flutter app but have strong concept in android/kotlin. I'm trying to understand the basic structure of the flutter app. I read that every widget need a build function to override to draw the children that was fine for me because in android/kotlin there is onCreate(); or similar others. Then I saw this code on the official document page.
void main() {
runApp(
Center(
child: Text(
'Hello, world!',
textDirection: TextDirection.ltr,
),
),
);
}
It is working fine without build() function so what is the real purpose of the build function? And when we need it? What can be without it or what can't?
While you could have everything passed directly to runApp, it has a pretty big drawback:
Your app would be static. Without a build function (or a builder like with FutureBuilder), then your app will have no way of having dynamic content.
It is also pretty bad for reusability. You may want to extract some part of this widget tree into custom widgets, to reuse them in different locations – which implies a build method for that custom widget.
Flutter functions rather differently from many other platforms, and the name build() only adds to the confusion. :-)) To understand it, try to forget your previous experiences with other platforms temporarily.
Build() is really more like display()
Flutter uses the build() system to display the current frame. If the app has rapidly changing content, like an animation or a game, build() will be called 60 or more times per second, that is, for every single frame. Yes, that's the intention: no matter what its name is, think about it as a function to displayCurrentFrame(), not what the name might imply, to build a widget and then use it for the rest of the life of your app.
The system is perfectly optimized to know when it has to call build(). For content that doesn't change that often, it will not call it 60 times per second, it's smart enough not to do that. But every time it's really needed, it will be called (and for really complicated cases, you also have mechanisms to help Flutter decide when to call and what to call, to make sure that only parts that really change get redrawn, making it possible to avoid jerkyness in apps that really need rapidly changing content, mostly games).
The task of your build() is to take whatever data you currently have (that's called the state of your widget) and build up the widget (or widgets) just with that data, just for that single display frame. Next time around, with possibly different data, you will build it again and again.
So, a Flutter app works differently from many other platforms. You don't write code that waits for interaction from the user, then, for instance, calls a display function to show a new selection, a new text entry, a new image, anything directly. All your widgets function like this: whenever there is a change, the user does something, a response arrives from a call you made over the internet, a timer has elapsed, so basically, anything happens, your widget stores whatever new data you just received into its own state and tells Flutter that "hey, there are changes, please, call me so that I can draw a current version of myself." And Flutter will call its build() all right, and your widget will display itself according to this current new data. Again and again, as long as your app is alive and kicking.
At first, all this might seem like a waste of resources. Why rebuild everything on potentially every frame, instead of building a widget, keeping it alive and using it while the app lasts? The only realistic answer is: don't worry. The system was conceived explicitely with that structure in mind, it gets compiled into optimized code that works just fine, it's fast and responsive. Those widgets are lightweight enough so that this doesn't mean the slightest problem in real life (and, as already mentioned, in really complex programs where it starts to matter, you can have your say in how it should work, but you don't have to worry about that, either, until you reach that stage when it really counts). Just get used to it and accept that this is the way Flutter works. :-)
Other implications
All this has other implications as well. You should never do anything in build() that you're not comfortable doing on every display cycle: both because it shouldn't take too much time and because it will be called over and over again. Especially not anything that's a longish operation. You may start an async operation (actually, you probably do so quite often when acting on some user input), but that's async, it goes away to do its job and simply returns later. When it does return, you store the new data in the state, as described above, and use that in build() the next time around. But you never wait for anything there, or do any real complex programming logic and perform tasks there. It's nothing more than a display(), really.
I clearly understand the difference between stateful and stateless widgets. But basically if I want, I can always use some statefulwidgets even if they will not be updated. What happen if I do that? Will there be a performance issue?
Nothing bad.
You'll have a small overhead, and StatelessWidget is slightly more performant (just slightly). But overall, nothing special.