About the benefits of const for Stateles widget in Flutter - flutter

There is a custom appbar:
class _MyAppBar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return SliverAppBar(
title: Text('Catalog', style: Theme.of(context).textTheme.headline1),
...
);
}
}
and usage of it
return Scaffold(
body: CustomScrollView(
slivers: [
_MyAppBar(),
const SliverToBoxAdapter(child: SizedBox(height: 12)),
...
In that code const using wildly (it's the Flutter team code as an example of a particular package usage) and why in this case const isn't used for _MyAppBar() (we could add const constructor in _MyAppBar definition)?

When you create a widget and is able to prefix it with const that widget will not rebuild. The framework knows not to rebuild such widgets because it can tell that the objects did not change. Constant objects are resolved at compile-time, and due to a process known as canonicalization if two or more objects have the same parameters, and are constant, they will refer to the same instance.
For example, if you have
#override
Widget build() {
const MyWidget();
}
And then somewhere you call setState, MyWidget will not be reconstructed because it was already resolved at compile-time, and the framework will also not call its build method which makes sense because non of the arguments passed to MyWidget (which are none here) has changed as a result of issuing the rebuild so the configuration is still the same.
Moreover if somewhere else you call const MyWidget(); you will still refer to the same object/instance so it's more optimal.
And they added the lint rule so that people add a const constructor and are able to invoke their widgets/classes with const and it uses by default in flutter. Flutter apps, packages, and plugins created with flutter create starting with Flutter version 2.3.0 are already set up to use the lints defined in this package.
Disabling individual rules
include: package:lints/recommended.yaml
linter:
rules:
avoid_shadowing_type_parameters: false
await_only_futures: true
For more read this analysis option and const keyword.
After reading this, it would be helped you to understand why in your case const isn't used for _MyAppBar().
SRC From: remove const

when you use const behind widgets it changes the way it is rebuilt it will not rebuild completely from scratch when you setState the widget.it uses the data that has been stored on ram and rebuilds it with that.
when your widget does not change with rebuilding the page it is recommended to improve the application behavoiur

Related

Is it a good practise to use many valuelistenable builders instead of setState in flutter?

I am making a flutter app and on one of the pages, I have many changing variables.
I am confused if I should use ValueListenableBuilder for all the variables or I should just use setState for all the variables.
It really depends. If it is a really simple widget with not that many variables changing, just use setState.
However, if you have MANY variables that has to change constantly. You'd better use ValueListenableBuilder, and actually that's one of the reasons to use the state management.
for example, take a look at the code below.
class SampleStatefulWidget extends StatefulWidget {
const SampleStatefulWidget({super.key});
#override
State<SampleStatefulWidget> createState() => _SampleStatefulWidgetState();
}
class _SampleStatefulWidgetState extends State<SampleStatefulWidget> {
final _variable1 = 'hello';
final _variable2 = 'world';
final _variable3 = 'foo';
final _variable4 = 'bar';
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
children: [
Text(_variable1),
Text(_variable2),
Text(_variable3),
Text(_variable4),
],
),
),
);
}
}
for the above code, if you use setState, the whole widget will be drawn again even though you just updated _variable1.
however, if you use ValueListenableBuilder, it will only update that part where _variable1 is in.
Therefore, if it is a complex widget with bunch of variables that constantly change, use some libraries that will help you manage states such as Provider or Riverpod. (I personally really don't recommend GetX)
or you can create your own BLoC for BLoC Pattern, or you can also use library for BLoC as well.

How to set state a class from another class in flutter?

I'm trying to implement dark mode in my flutter app using provider, but I'm having some issues:
I have a home screen that it has a scaffold widget. The body of the scaffold is a Stack with two different classes, as you can see here:
#override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: Constant.menuOn
? BorderRadius.circular(60.0)
: BorderRadius.circular(0.0),
child: Scaffold(
body: Stack(
children: const [
Menu(),
HomeScreen(),
],
),
),
);
}
The home screen class has another Scaffold widget, where hos body has another class called body.
It is from the body where I'm able to change the theme of my app using a switch where I can set state my BODY class with the provider as you can see here:
lsetState(() {
final provider = Provider.of<ThemeProvider>(
context,
listen: false);
rovider.toggleTheme(Constant.isDarkMode);
});
My body class change the theme however the class that is under the body class (Remember that my class "MENU" is under the class BODY because the first stake), does not change theme until restart my app (I'm using the same parameters of the body in order to know when has to change the class)...
I want to know how I can set the theme state of my menu class, or in general how I can change any other state from any class.
Note: Both of my class are StatefulWidget.
In general, setState is a very local intra-widget operation. Local data has changed, so the local view needs updating. Wanting to call the setState of another widget is a bad code smell. If you need other views depending on data you've updated, consider one of the state management solutions. (I would, for example, use a Riverpod Provider to hold the data, then watch that in every dependent view.)

Dart : Object vs Function what's the best implementation choice [duplicate]

I have realized that it is possible to create widgets using plain functions instead of subclassing StatelessWidget. An example would be this:
Widget function({ String title, VoidCallback callback }) {
return GestureDetector(
onTap: callback,
child: // some widget
);
}
This is interesting because it requires far less code than a full-blown class. Example:
class SomeWidget extends StatelessWidget {
final VoidCallback callback;
final String title;
const SomeWidget({Key key, this.callback, this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: callback,
child: // some widget
);
}
}
So I've been wondering: Is there any difference besides syntax between functions and classes to create widgets? And is it a good practice to use functions?
Edit: The Flutter team has now taken an official stance on the matter and stated that classes are preferable. See https://www.youtube.com/watch?v=IOyq-eTRhvo
TL;DR: Prefer using classes over functions to make reusable widget-tree.
EDIT: To make up for some misunderstanding:
This is not about functions causing problems, but classes solving some.
Flutter wouldn't have StatelessWidget if a function could do the same thing.
Similarly, it is mainly directed at public widgets, made to be reused. It doesn't matter as much for private functions made to be used only once – although being aware of this behavior is still good.
There is an important difference between using functions instead of classes, that is: The framework is unaware of functions, but can see classes.
Consider the following "widget" function:
Widget functionWidget({ Widget child}) {
return Container(child: child);
}
used this way:
functionWidget(
child: functionWidget(),
);
And it's class equivalent:
class ClassWidget extends StatelessWidget {
final Widget child;
const ClassWidget({Key key, this.child}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: child,
);
}
}
used like that:
new ClassWidget(
child: new ClassWidget(),
);
On paper, both seem to do exactly the same thing: Create 2 Container, with one nested into the other. But the reality is slightly different.
In the case of functions, the generated widget tree looks like this:
Container
Container
While with classes, the widget tree is:
ClassWidget
Container
ClassWidget
Container
This is important because it changes how the framework behaves when updating a widget.
Why that matters
By using functions to split your widget tree into multiple widgets, you expose yourself to bugs and miss on some performance optimizations.
There is no guarantee that you will have bugs by using functions, but by using classes, you are guaranteed to not face these issues.
Here are a few interactive examples on Dartpad that you can run yourself to better understand the issues:
https://dartpad.dev/?id=bcae5878ccced764b35dd9a659a593db
This example showcases how by splitting your app into functions,
you may accidentally break things like AnimatedSwitcher
https://dartpad.dev/?id=481a2c301c2e4bed6c30ba651d01bacb
This example showcases how classes allow more granular rebuilds of the
widget tree, improving performances
https://dartpad.dev/?id=8bcb85ba535102bed652e5bf1540ac3b
This example showcases how, by using functions, you expose yourself
to misusing BuildContext and facing bugs when using InheritedWidgets (such as Theme or providers)
Conclusion
Here's a curated list of the differences between using functions and classes:
Classes:
allow performance optimization (const constructor, more granular rebuild)
ensure that switching between two different layouts correctly disposes of the resources (functions may reuse some previous state)
ensures that hot-reload works properly (using functions could break hot-reload for showDialogs & similar)
are integrated into the widget inspector.
We see ClassWidget in the widget-tree showed by the devtool, which
helps understanding what is on screen
We can override debugFillProperties to print what the parameters passed to a widget are
better error messages
If an exception happens (like ProviderNotFound), the framework will give you the name of the currently building widget.
If you've split your widget tree only in functions + Builder, your errors won't have a helpful name
can define keys
can use the context API
Functions:
have less code (which can be solved using code-generation functional_widget)
Overall, it is considered a bad practice to use functions over classes for reusing widgets because of these reasons.
You can, but it may bite you in the future.
I've been researching on this issue for the past 2 days. I came to the following conclusion: it is OKAY to break down pieces of the app into functions. It's just ideal that those functions return a StatelessWidget, so optimisations can be made, such as making the StatelessWidget const, so it doesn't rebuild if it doesn't have to.
For example, this piece of code is perfectly valid:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
++_counter;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
const MyWidgetClass(key: const Key('const')),
MyWidgetClass(key: Key('non-const')),
_buildSomeWidgets(_counter),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Widget _buildSomeWidgets(int val) {
print('${DateTime.now()} Rebuild _buildSomeWidgets');
return const MyWidgetClass(key: Key('function'));
// This is bad, because it would rebuild this every time
// return Container(
// child: Text("hi"),
// );
}
}
class MyWidgetClass extends StatelessWidget {
const MyWidgetClass({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
print('${DateTime.now()} Rebuild MyWidgetClass $key');
return Container(
child: Text("hi"),
);
}
}
The use of function there is perfectly fine, as it returns a const StatelessWidget. Please correct me if I'm wrong.
1 - Most of the time build method (child widget) calls number of synchronous and asynchronous functions.
Ex:
To download network image
get input from users etc.
so build method need to keep in the separate class widget (because all other methods call by build() method can keep in one class)
2 - Using the widget class you can create a number of other classes without writing the same code again and again (** Use Of Inheritance** (extends)).
And also using inheritance(extend) and polymorphism (override) you can create your own custom class.
(Down below example, In there I will customize (Override) the animation by extending MaterialPageRoute (because its default transition I want to customize).👇
class MyCustomRoute<T> extends MaterialPageRoute<T> {
MyCustomRoute({ WidgetBuilder builder, RouteSettings settings })
: super(builder: builder, settings: settings);
#override //Customize transition
Widget buildTransitions(BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
if (settings.isInitialRoute)
return child;
// Fades between routes. (If you don't want any animation,
// just return child.)
return new FadeTransition(opacity: animation, child: child);
}
}
3 - Functions cannot add conditions for their parameters, But using the class widget's constructor You can do this.
Down below Code example👇 (this feature is heavily used by framework widgets)
const Scaffold({
Key key,
this.bottomNavigationBar,
this.bottomSheet,
this.backgroundColor,
this.resizeToAvoidBottomPadding,
this.resizeToAvoidBottomInset,
this.primary = true,
this.drawerDragStartBehavior = DragStartBehavior.start,
this.extendBody = false,
this.extendBodyBehindAppBar = false,
this.drawerScrimColor,
this.drawerEdgeDragWidth,
}) : assert(primary != null),
assert(extendBody != null),
assert(extendBodyBehindAppBar != null),
assert(drawerDragStartBehavior != null),
super(key: key);
4 - Functions cannot use const and the Class widget can use the const for their constructors.
(that affect the performance of the main thread)
5 - You can create any number of independent widgets using the same class (instances of a class/objects)
But function cannot create independant widgets(instance), but reusing can.
[each instance has its own instance variable and that is completely independant from other widgets(object), But function's local variable is dependant on each function call* (which means, when you change a value of a local variable it affect for all other parts of the application which use this function)]
There were many Advantages in class over functions..(above are few use cases only)
My Final Thought
So don't use Functions as building blocks of your application, use them only for doing Operations.
Otherwise, it causes many unchangeable problems when your application gets scalable.
Use functions for doing a small portion of the task
Use class as a building block of an application(Managing application)
As Remi has eloquently put repeatedly, it's not the functions by themselves that cause a problem, the problem is us thinking that using a function has a similar benefit to using a new widget.
Unfortunately this advice is evolving into "the act of merely using a function is inefficient", with often incorrect speculations into why this might be.
Using a function is almost the same as using what the function returns in place of that function. So, if you are calling a widget constructor and giving it as a child to another widget, you are not making your code inefficient by moving that constructor call into a function.
//...
child: SomeWidget(),
//...
is not significantly better in terms of efficiency than
//...
child: buildSomeWidget();
//...
Widget buildSomeWidget() => SomeWidget();
It is fine to argue the following about the second one:
It's ugly
It's unnecessary
I don't like it
Function does not appear in Flutter Inspector
Two functions may not work with AnimatedSwitcher et al.
It does not create a new context, so you can't reach the Scaffold above it through context
If you use ChangeNotifier in it, its rebuild is not contained within the function
But it's not correct to argue this:
Using a function is inefficient in terms of performance
Creating a new widget brings these performance benefits:
ChangeNotifier within it does not make its parent rebuild upon changes
Sibling widgets are protected from each other's rebuilds
Creating it with const (if possible) protects it from parent's rebuilds
You are more likely to keep your const constructor if you can isolate the changing children to other widgets
However, if you do not have any of these cases, and your build function is looking more and more like pyramid of doom, it is better to refactor a part of it to a function rather than keeping the pyramid. Especially if you are enforcing 80 character limit, you may find yourself writing code in about 20 character-wide space. I see a lot of newbies falling into this trap. The message to those newbies should be "You should really be creating new widgets here. But if you can't, at least create a function.", not "You have to create a widget or else!". Which is why I think we have to be more specific when we promote widgets over functions and avoid being factually incorrect about efficiency.
For your convenience, I have refactored Remi's code to show that the problem is not simply using functions, but the problem is avoiding creating new widgets. So, if you were to place the widget-creating code in those functions into where the functions are called (refactor-inline) you have the exact same behavior as using functions, but without using functions! So, it's not using functions that's the problem, it's the avoidance of creating new widget classes.
(remember to turn off null safety as the original code is from 2018)
Here are a few interactive examples on Dartpad that you can run
yourself to better understand the issues:
https://dartpad.dev/1870e726d7e04699bc8f9d78ba71da35 This example
showcases how by splitting your app into functions, you may
accidentally break things like AnimatedSwitcher
Non-function version: https://dartpad.dev/?id=ae5686f3f760e7a37b682039f546a784
https://dartpad.dev/a869b21a2ebd2466b876a5997c9cf3f1 This example
showcases how classes allow more granular rebuilds of the widget tree,
improving performances
Non-function version: https://dartpad.dev/?id=795f286791110e3abc1900e4dcd9150b
https://dartpad.dev/06842ae9e4b82fad917acb88da108eee This example
showcases how, by using functions, you expose yourself to misusing
BuildContext and facing bugs when using InheritedWidgets (such as
Theme or providers)
Non-function version: https://dartpad.dev/?id=65f753b633f68503262d5adc22ea27c0
You will find that not having them in a function creates the exact same behavior. So it's adding widgets that gives you the win. It's not adding functions that creates a problem.
So the suggestions should be:
Avoid the pyramid of doom at any cost! You need horizontal space to code. Don't get stuck at the right margin.
Create functions if you need, but do not give parameters to them as it's impossible to find the line that calls the function through Flutter Inspector.
Consider creating new widget classes, it's the better way! Try Refactor->Extract Flutter Widget. You won't be able to if your code is too coupled with the current class. Next time you should plan better.
Try to comment out things that prevent you from extracting a new widget. Most likely they are function calls in the current class (setState, etc.). Extract your widget then, and find ways of adding that stuff in. Passing functions to the constructor may be ok (think onPressed). Using a state management system may be even better.
I hope this can help remind why we prefer widgets over functions and that simply using a function is not a huge problem.
Edit: one point that was missed in this whole discussion: when you widgetize, siblings don't rebuild each other anymore. This Dartpad demonstrates this: https://dartpad.dartlang.org/?id=8d9b6d5bd53a23b441c117cd95524892
When you are calling the Flutter widget make sure you use the const keyword. For example const MyListWidget();
In case this helps anyone passing this way, some things I have in my conceptual model of Flutter developed from this question and working with Flutter in general (caveat: I could still be deeply confused and wrong about this stuff).
A Widget is what you want and the Elements are what you have. It is the job of the rendering engine to reconcile the two as efficiently as possible.
Use Keys, they can help a lot.
A BuildContext is an Element.
Any Thing.of(context) is likely to introduce a build dependency. If Thing changes it will trigger a rebuild from the context element.
In your build() if you access a BuildContext from a nested widget you are acting on the Element at the top of your subtree.
Widget build(BuildContext rootElement) {
return Container(
child:Container(
child:Container(
child:Text(
"Depends on rootElement",
// This introduces a build trigger
// If ThemeData changes a rebuild is triggered
// on rootElement not this Text()-induced element
style:Theme.of(rootElement).textTheme.caption,
),
),
),
);
}
AnimatedSwitcher is a slippery beast - it has to be able to distinguish its children. You can use functions if they return different types or return the same type but with different Keys
If you are authoring a Widget use a class not a Function but feel free to refactor your 1000 line build() method with functions/methods, the outcome is identical*.
* but could be even better to refactor into classes

Flutter - Keep page static throughout lifecycle of app?

I have created an AppDrawer widget to wrap my primary drawer navigation and reference it in a single place, like so:
class AppDrawer extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Drawer(
child: new ListView(
children: <Widget>[
new ListTile(
title: new Text("Page1"),
trailing: new Icon(Icons.arrow_right),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context) => Page1.singleInstance));
}
),
new ListTile(
title: new Text("Page2"),
trailing: new Icon(Icons.arrow_right),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).push(new MaterialPageRoute(builder: (BuildContext context) => new Page2("Page 2")));
}
),
]
),
);
}
}
I have also created a custom AppScaffold widget, which simply returns a consistent AppBar, my custom AppDrawer, and body:
class AppScaffold extends StatelessWidget {
final Widget body;
final String pageTitle;
AppScaffold({this.body, this.pageTitle});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(title: new Text(pageTitle), backgroundColor: jet),
drawer: AppDrawer(),
body: body
);
}
}
I have created two pages: Page1, and Page2. They are simple right now, and look something like this:
class Page1 extends StatelessWidget {
final String pageText;
Page1(this.pageText);
static Page1 get singleInstance => Page1("Page1");
Widget build(BuildContext context) {
return AppScaffold(
pageTitle: this.pageText,
body: SafeArea(
child: Stack(
children: <Widget>[
Center(child: SomeCustomWidget())
],
)
),
);
}
}
class Page2 extends StatelessWidget {
final String pageText;
Page2(this.pageText);
#override
Widget build(BuildContext context) {
return AppScaffold(
pageTitle: this.pageText,
body: SafeArea(
child: Stack(
children: <Widget>[
Center(child: SomeOtherCustomWidget())
],
)
),
);
}
}
When I run my app, I can see the navbar and drawer correctly. I can click on the links in the drawer to navigate between my pages. However, each time I navigate to a page, all of the widgets on that page get reset to their initial state. I want to ensure that the widgets do not get reset. Another way to think of this is: I only want one instance of each page throughout the lifecycle of the app, instead of creating them new whenever a user navigates to them.
I tried creating a static instance of Page1 that the Drawer uses when the onTap event is fired, but this does not work. Am I thinking about this incorrectly? Do I need to convert to a Stateful widget?
Oh, you're in for a treat... This will be kinda long (sorry) but please read all of it before making decisions and taking action - I promise I am saving you time.
There are many different solutions to this problem, but in general what you're asking about is state management (which is really software engineering, more info here - Understanding state management, and why you never will).
I'll try my best to explain what is happening in your specific case...
Problem:
Think of Navigator as a List of application states, which you can manipulate via its various methods (i.e. pop(), push(), etc.), with this in mind it is clear what is happening - on a button press you're actually removing the current state (page) and right after that you're pushing a new instance of your state (page).
Solution(s):
As I said, there are many solutions to this problem, for example, you may be tempted to store the state (the changes you made to a particular "page") somewhere in a var and inject that var when navigating between "pages", when creating a new instance of that page, but you'll soon run into other problems. This is why I don't think anyone can provide a simple solution to this problem...
First, may I suggest you some useful reads on the matter:
Flutter official docs on state management - When you get to the "Options" section of this, the fun part begins and can quickly get overwhelming, but fear not :P
Be sure to read the medium article mentioned in the start of my answer too, I found it really helpful.
These reads will be more than enough to help you make a decision, plus there are a ton of articles on Medium and YouTube videos touching on the matter of state management with Flutter (even some from the authors of the framework) - just search for "State management with Flutter".
Now my own personal opinion:
If it's a really simple use case and you don't plan to grow (which is almost never the case, trust me), you can just use StatefulWidgets in combination with setState() and maybe InheritedWidget (for dependency injection down the tree, or like React guys call it "lifting state up"). Or instead of the above, maybe have a look at scoped_model, which kinda abstracts all of this for you (tho, I haven't played with it).
What I use right now for a real world project is bloc and flutter_bloc (BLoC = Business Logic Component), I will not get into the details of it, but basically it takes the idea of scoped_model one step further, without over-complicating abstractions. bloc is responsible for abstracting away the "business logic" of your application and flutter_bloc to "inject" the state in your UI and react to state changes (official Flutter position on the matter is that UI = f(State)).
A BLoC has an input and an output, it takes in events as an input (can be user input, or other, any type of event really) and produces a state. In summary that's it about bloc.
A great way to get started is BLoC's official documentation. I highly recommend it. Just go through everything.
(p.s. This may be my personal opinion, but in the end state management in Flutter is all based on some form of using InheritedWidget and setState() in response to user input or other external factors that should change the application state, so I think the BLoC pattern is really on point with abstracting those :P)

What is the difference between functions and classes to create reusable widgets?

I have realized that it is possible to create widgets using plain functions instead of subclassing StatelessWidget. An example would be this:
Widget function({ String title, VoidCallback callback }) {
return GestureDetector(
onTap: callback,
child: // some widget
);
}
This is interesting because it requires far less code than a full-blown class. Example:
class SomeWidget extends StatelessWidget {
final VoidCallback callback;
final String title;
const SomeWidget({Key key, this.callback, this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: callback,
child: // some widget
);
}
}
So I've been wondering: Is there any difference besides syntax between functions and classes to create widgets? And is it a good practice to use functions?
Edit: The Flutter team has now taken an official stance on the matter and stated that classes are preferable. See https://www.youtube.com/watch?v=IOyq-eTRhvo
TL;DR: Prefer using classes over functions to make reusable widget-tree.
EDIT: To make up for some misunderstanding:
This is not about functions causing problems, but classes solving some.
Flutter wouldn't have StatelessWidget if a function could do the same thing.
Similarly, it is mainly directed at public widgets, made to be reused. It doesn't matter as much for private functions made to be used only once – although being aware of this behavior is still good.
There is an important difference between using functions instead of classes, that is: The framework is unaware of functions, but can see classes.
Consider the following "widget" function:
Widget functionWidget({ Widget child}) {
return Container(child: child);
}
used this way:
functionWidget(
child: functionWidget(),
);
And it's class equivalent:
class ClassWidget extends StatelessWidget {
final Widget child;
const ClassWidget({Key key, this.child}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: child,
);
}
}
used like that:
new ClassWidget(
child: new ClassWidget(),
);
On paper, both seem to do exactly the same thing: Create 2 Container, with one nested into the other. But the reality is slightly different.
In the case of functions, the generated widget tree looks like this:
Container
Container
While with classes, the widget tree is:
ClassWidget
Container
ClassWidget
Container
This is important because it changes how the framework behaves when updating a widget.
Why that matters
By using functions to split your widget tree into multiple widgets, you expose yourself to bugs and miss on some performance optimizations.
There is no guarantee that you will have bugs by using functions, but by using classes, you are guaranteed to not face these issues.
Here are a few interactive examples on Dartpad that you can run yourself to better understand the issues:
https://dartpad.dev/?id=bcae5878ccced764b35dd9a659a593db
This example showcases how by splitting your app into functions,
you may accidentally break things like AnimatedSwitcher
https://dartpad.dev/?id=481a2c301c2e4bed6c30ba651d01bacb
This example showcases how classes allow more granular rebuilds of the
widget tree, improving performances
https://dartpad.dev/?id=8bcb85ba535102bed652e5bf1540ac3b
This example showcases how, by using functions, you expose yourself
to misusing BuildContext and facing bugs when using InheritedWidgets (such as Theme or providers)
Conclusion
Here's a curated list of the differences between using functions and classes:
Classes:
allow performance optimization (const constructor, more granular rebuild)
ensure that switching between two different layouts correctly disposes of the resources (functions may reuse some previous state)
ensures that hot-reload works properly (using functions could break hot-reload for showDialogs & similar)
are integrated into the widget inspector.
We see ClassWidget in the widget-tree showed by the devtool, which
helps understanding what is on screen
We can override debugFillProperties to print what the parameters passed to a widget are
better error messages
If an exception happens (like ProviderNotFound), the framework will give you the name of the currently building widget.
If you've split your widget tree only in functions + Builder, your errors won't have a helpful name
can define keys
can use the context API
Functions:
have less code (which can be solved using code-generation functional_widget)
Overall, it is considered a bad practice to use functions over classes for reusing widgets because of these reasons.
You can, but it may bite you in the future.
I've been researching on this issue for the past 2 days. I came to the following conclusion: it is OKAY to break down pieces of the app into functions. It's just ideal that those functions return a StatelessWidget, so optimisations can be made, such as making the StatelessWidget const, so it doesn't rebuild if it doesn't have to.
For example, this piece of code is perfectly valid:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
++_counter;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
const MyWidgetClass(key: const Key('const')),
MyWidgetClass(key: Key('non-const')),
_buildSomeWidgets(_counter),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
Widget _buildSomeWidgets(int val) {
print('${DateTime.now()} Rebuild _buildSomeWidgets');
return const MyWidgetClass(key: Key('function'));
// This is bad, because it would rebuild this every time
// return Container(
// child: Text("hi"),
// );
}
}
class MyWidgetClass extends StatelessWidget {
const MyWidgetClass({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
print('${DateTime.now()} Rebuild MyWidgetClass $key');
return Container(
child: Text("hi"),
);
}
}
The use of function there is perfectly fine, as it returns a const StatelessWidget. Please correct me if I'm wrong.
1 - Most of the time build method (child widget) calls number of synchronous and asynchronous functions.
Ex:
To download network image
get input from users etc.
so build method need to keep in the separate class widget (because all other methods call by build() method can keep in one class)
2 - Using the widget class you can create a number of other classes without writing the same code again and again (** Use Of Inheritance** (extends)).
And also using inheritance(extend) and polymorphism (override) you can create your own custom class.
(Down below example, In there I will customize (Override) the animation by extending MaterialPageRoute (because its default transition I want to customize).👇
class MyCustomRoute<T> extends MaterialPageRoute<T> {
MyCustomRoute({ WidgetBuilder builder, RouteSettings settings })
: super(builder: builder, settings: settings);
#override //Customize transition
Widget buildTransitions(BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child) {
if (settings.isInitialRoute)
return child;
// Fades between routes. (If you don't want any animation,
// just return child.)
return new FadeTransition(opacity: animation, child: child);
}
}
3 - Functions cannot add conditions for their parameters, But using the class widget's constructor You can do this.
Down below Code example👇 (this feature is heavily used by framework widgets)
const Scaffold({
Key key,
this.bottomNavigationBar,
this.bottomSheet,
this.backgroundColor,
this.resizeToAvoidBottomPadding,
this.resizeToAvoidBottomInset,
this.primary = true,
this.drawerDragStartBehavior = DragStartBehavior.start,
this.extendBody = false,
this.extendBodyBehindAppBar = false,
this.drawerScrimColor,
this.drawerEdgeDragWidth,
}) : assert(primary != null),
assert(extendBody != null),
assert(extendBodyBehindAppBar != null),
assert(drawerDragStartBehavior != null),
super(key: key);
4 - Functions cannot use const and the Class widget can use the const for their constructors.
(that affect the performance of the main thread)
5 - You can create any number of independent widgets using the same class (instances of a class/objects)
But function cannot create independant widgets(instance), but reusing can.
[each instance has its own instance variable and that is completely independant from other widgets(object), But function's local variable is dependant on each function call* (which means, when you change a value of a local variable it affect for all other parts of the application which use this function)]
There were many Advantages in class over functions..(above are few use cases only)
My Final Thought
So don't use Functions as building blocks of your application, use them only for doing Operations.
Otherwise, it causes many unchangeable problems when your application gets scalable.
Use functions for doing a small portion of the task
Use class as a building block of an application(Managing application)
As Remi has eloquently put repeatedly, it's not the functions by themselves that cause a problem, the problem is us thinking that using a function has a similar benefit to using a new widget.
Unfortunately this advice is evolving into "the act of merely using a function is inefficient", with often incorrect speculations into why this might be.
Using a function is almost the same as using what the function returns in place of that function. So, if you are calling a widget constructor and giving it as a child to another widget, you are not making your code inefficient by moving that constructor call into a function.
//...
child: SomeWidget(),
//...
is not significantly better in terms of efficiency than
//...
child: buildSomeWidget();
//...
Widget buildSomeWidget() => SomeWidget();
It is fine to argue the following about the second one:
It's ugly
It's unnecessary
I don't like it
Function does not appear in Flutter Inspector
Two functions may not work with AnimatedSwitcher et al.
It does not create a new context, so you can't reach the Scaffold above it through context
If you use ChangeNotifier in it, its rebuild is not contained within the function
But it's not correct to argue this:
Using a function is inefficient in terms of performance
Creating a new widget brings these performance benefits:
ChangeNotifier within it does not make its parent rebuild upon changes
Sibling widgets are protected from each other's rebuilds
Creating it with const (if possible) protects it from parent's rebuilds
You are more likely to keep your const constructor if you can isolate the changing children to other widgets
However, if you do not have any of these cases, and your build function is looking more and more like pyramid of doom, it is better to refactor a part of it to a function rather than keeping the pyramid. Especially if you are enforcing 80 character limit, you may find yourself writing code in about 20 character-wide space. I see a lot of newbies falling into this trap. The message to those newbies should be "You should really be creating new widgets here. But if you can't, at least create a function.", not "You have to create a widget or else!". Which is why I think we have to be more specific when we promote widgets over functions and avoid being factually incorrect about efficiency.
For your convenience, I have refactored Remi's code to show that the problem is not simply using functions, but the problem is avoiding creating new widgets. So, if you were to place the widget-creating code in those functions into where the functions are called (refactor-inline) you have the exact same behavior as using functions, but without using functions! So, it's not using functions that's the problem, it's the avoidance of creating new widget classes.
(remember to turn off null safety as the original code is from 2018)
Here are a few interactive examples on Dartpad that you can run
yourself to better understand the issues:
https://dartpad.dev/1870e726d7e04699bc8f9d78ba71da35 This example
showcases how by splitting your app into functions, you may
accidentally break things like AnimatedSwitcher
Non-function version: https://dartpad.dev/?id=ae5686f3f760e7a37b682039f546a784
https://dartpad.dev/a869b21a2ebd2466b876a5997c9cf3f1 This example
showcases how classes allow more granular rebuilds of the widget tree,
improving performances
Non-function version: https://dartpad.dev/?id=795f286791110e3abc1900e4dcd9150b
https://dartpad.dev/06842ae9e4b82fad917acb88da108eee This example
showcases how, by using functions, you expose yourself to misusing
BuildContext and facing bugs when using InheritedWidgets (such as
Theme or providers)
Non-function version: https://dartpad.dev/?id=65f753b633f68503262d5adc22ea27c0
You will find that not having them in a function creates the exact same behavior. So it's adding widgets that gives you the win. It's not adding functions that creates a problem.
So the suggestions should be:
Avoid the pyramid of doom at any cost! You need horizontal space to code. Don't get stuck at the right margin.
Create functions if you need, but do not give parameters to them as it's impossible to find the line that calls the function through Flutter Inspector.
Consider creating new widget classes, it's the better way! Try Refactor->Extract Flutter Widget. You won't be able to if your code is too coupled with the current class. Next time you should plan better.
Try to comment out things that prevent you from extracting a new widget. Most likely they are function calls in the current class (setState, etc.). Extract your widget then, and find ways of adding that stuff in. Passing functions to the constructor may be ok (think onPressed). Using a state management system may be even better.
I hope this can help remind why we prefer widgets over functions and that simply using a function is not a huge problem.
Edit: one point that was missed in this whole discussion: when you widgetize, siblings don't rebuild each other anymore. This Dartpad demonstrates this: https://dartpad.dartlang.org/?id=8d9b6d5bd53a23b441c117cd95524892
When you are calling the Flutter widget make sure you use the const keyword. For example const MyListWidget();
In case this helps anyone passing this way, some things I have in my conceptual model of Flutter developed from this question and working with Flutter in general (caveat: I could still be deeply confused and wrong about this stuff).
A Widget is what you want and the Elements are what you have. It is the job of the rendering engine to reconcile the two as efficiently as possible.
Use Keys, they can help a lot.
A BuildContext is an Element.
Any Thing.of(context) is likely to introduce a build dependency. If Thing changes it will trigger a rebuild from the context element.
In your build() if you access a BuildContext from a nested widget you are acting on the Element at the top of your subtree.
Widget build(BuildContext rootElement) {
return Container(
child:Container(
child:Container(
child:Text(
"Depends on rootElement",
// This introduces a build trigger
// If ThemeData changes a rebuild is triggered
// on rootElement not this Text()-induced element
style:Theme.of(rootElement).textTheme.caption,
),
),
),
);
}
AnimatedSwitcher is a slippery beast - it has to be able to distinguish its children. You can use functions if they return different types or return the same type but with different Keys
If you are authoring a Widget use a class not a Function but feel free to refactor your 1000 line build() method with functions/methods, the outcome is identical*.
* but could be even better to refactor into classes