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.
Related
I have a Flutter app which is becoming more and more janky as time goes by and more features are added. Therefore, is there some utility to make it as smooth as 60FPS?
I know there are some official guides here: https://docs.flutter.dev/perf. However, I have tried to optimize and it is still slow. You know, some things just cannot be fast enough, such as long text, dynamic layout, necessary synchronous computations, etc. Especially when entering a new page or scrolling down a ListView. In addition, I have to use brainpower to find out what is slow and optimize when new features are added, so I hope there is some fully automatic thing which I can drop-in replace and forget it and it just works forever.
Disclaimer: I wrote this package and this is a Q&A style StackOverflow answer.
Yes, I have made it: https://github.com/fzyzcjy/flutter_smooth.
No matter how heavy the tree is to build/layout, it will run at (roughly) full FPS, feel smooth, has zero uncomfortable janks, with neglitable overhead. (I have made some benchmark reports here)
As for usage, for common scenarios, add 6 characters ("Smooth") - ListView becomes SmoothListView, MaterialPageRoute becomes SmoothMaterialPageRoute. For complex cases, use SmoothBuilder(builder: ...) and put whatever you want to be smooth inside the builder.
Roughly speaking about the implementation, it is done by submitting extra frames to the rasterizer every ~16ms, without disturbing all existing code. Therefore, the existing app code will almost not even know the existence of this package.
You need to check how do you use widgets, unnecessary rebuilds, some heavy operations when the widget is creating o rebuilding and it’s recommended to use the performance profiler in the devtools.
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.
Camera usage is clearly documented at official document. It tells you to initiate camera from the Future<void> main() async which is the main entry point of the whole application. (correct me if this is wrong). After that, the initialised camera is then passed down to the home page widget. This is all that official document tell us. But obviously, this isn't close enough to real-world cases. Rarely there is an application needs to open camera on the very first running.
This confuses me a lot. I can get this to work by passing down down this camera object to whatever pages that actually required it. Or maybe put it into a global state management like redux. But is this the correct way to use camera?
More ideally, if possible, I think it's the duty of a page/widget that needs camera to initiate everything rather than at the main() function.
Any suggestion are appreciated.
You should initialize the Camera plugin where you need it not in the main function. docs just give us an example of how to do that not where to.
I am currently writing an app, and records some info (eg GPS), even if screen is off (ie its screen has timed out in sleep mode).
It performs a setState() every so often to update the Widgets.
However, if the screen is asleep/inactive, I'm not sure there is a need to call setState(), or certain other cpu intensive actions, and may be better to preserve battery by not calling it ?
So, I am wondering if there is any way to detect if the screen is off, and hence not call setState..
eg in pseudocode
if( checkScreenIsOn() ) { setState((){...})}
I'm unsure if some event is triggered when the screen goes off that flutter sees. Also if things like GPS, I'd like to record normally when the screen is off (I'm currently using a listener), but not update the display. Does GPS go into a different mode, and any way to stop that if so ?
StatefullWidget have a mounted flag mounted property
Instead of if (checkScreenIsOn()) just do if (mounted) setState((){...})
If you want to know screen state you must use a MethodChannel to communicate with native api, eg. https://pub.dartlang.org/packages/screen_state
I want to make a little in-app demo like Tapbots does in Convertbot. Maybe there is a better solution than mine?
make everything programmatically controlable
write a huge class with hundreds of performSelector:withObject:afterDelay: calls to control the whole app for the demo
The demo actually only does two things:
Simulate touches on controls (i.e. programmatically pressing buttons)
Show text message bubbles when appropriate to explain what is going on
How would you do it?
I don't think there is an easy way to accomplish this.
My suggestion would be to create a class that runs a script of actions for you. The script itself could be as simple as an NSArray of objects representing steps in the demo, each with values such as text for a callout bubble, an action/target pairing (for calling selectors), delay, and so forth. Use NSButton setHighlighted: to simulate button presses. Your class then runs through the array of steps to conduct the demo. You could code this directly, or construct the script at runtime from a YAML file (or other file format that you find easy to edit).
I would expect that investing some time in a mechanism like this will make your life a lot easier when it comes time to a) write and b) fine tune your demo, particularly down the road when you want to add features. You don't want to be managing a huge list of hardcoded calls. And you might even be able to re-use the demo-running code on other projects.