Can Flutter be fast compared to other hybrid platforms? The answer is yes, but with that in mind, let's take a look at some amazing performance and optimization practices.
original
https://inficial.medium.com/flutter-best-practices-for-improve-performance-7e21e14efebb
refer to
- https://api.flutter.dev/flutter/widgets/ListView/itemExtent.html
- https://blog.codemagic.io/how-to-improve-the-performance-of-your-flutter-app./#dont-split-your-widgets-into-methods
- https://pub.dev/packages/nil
- https://medium.com/flutter-community/flutter-memory-optimization-series-8c4a73f3ea81
- https://dart.dev/guides/language/effective-dart/style
- https://dart-lang.github.io/linter/lints/index.html
- https://itnext.io/comparing-darts-loops-which-is-the-fastest-731a03ad42a2
- https://iisprey.medium.com/get-rid-of-all-kind-of-boilerplate-code-with-flutter-hooks-2e17eea06ca0
text
1. Use Widgets instead of functions
don't use it like this
Widget _buildFooterWidget() {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text('This is the footer '),
);
}.
use it like this
class FooterWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text('This is the footer '),
);
}
}
If a function could do the same thing, Flutter wouldn't have a StatelessWidget.
Similarly, it is mainly for public widgets, which can be reused. It doesn't matter that a private function can only be used once — though it's still good to be aware of this behavior.
There is an important difference between using functions instead of classes, and that is: the framework doesn't know about functions, but can see classes.
Consider the following "widget" function:
Widget functionWidget({ Widget child}) {
return Container(child: child);
}
Use like this:
functionWidget(
child : functionWidget()
)
And it's 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,
);
}
}
Use like this:
new ClassWidget(
child : new ClassWidget(),
)
On paper, both seem to do the same thing: create 2 containers, one nested within the other. But the reality is slightly different.
For functions, the resulting widgets tree looks like this:
Container
Container
For classes, the widgets tree is:
ClassWidget
Container
ClassWidget
Container
This is important because it changes the behavior of the framework when updating widgets.
why this matters
By using a function to split the widget tree into widgets, you are exposing yourself to bugs and missing out on some performance optimizations.
Using functions is not guaranteed to have bugs, but using classes guarantees that you will not encounter these problems.
the issues:
Here are some interactive examples on Dartpad that you can run yourself to better understand these issues:
- https://dartpad.dev/1870e726d7e04699bc8f9d78ba71da35 This example shows how by splitting your app's functionality you could accidentally break things like AnimatedSwitcher
- https://dartpad.dev/a869b21a2ebd2466b876a5997c9cf3f1
This example shows how classes allow a more fine-grained rebuild of the widgets tree, improving performance - https://dartpad.dev/06842ae9e4b82fad917acb88da108eee
This example shows how to expose yourself to incorrect usage of BuildContext and errors by using functions when using InheritedWidgets (such as themes or providers)
In general, using functions on classes to reuse widgets is considered a bad practice for these reasons. You can, but it might bite you in the future.
- Avoid rebuilding all widgets repeatedly
- Also, adding const is a good idea.
- User item range for a ListView long list.
Specifying itemExtent is more efficient than letting the sub-scroll mechanism determine its extent, because the scroll mechanism can take advantage of the sub-scroll mechanism's prior knowledge to save work, such as when the scroll position changes drastically.
- Avoid rebuilding unnecessary widgets in animatedBuilder
don't use it like this
body: AnimatedBuilder(
animation: _controller,
builder: (_, child) => Transform(
alignment: Alignment.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(360 * _controller.value * (pi / 180.0)),
child: CounterWidget(
counter: counter,
),),
),
This will rebuild the CounterWidget widgets when the animation occurs. If you put the log in the counterWidget's build method, you can see that it prints a log every time the animation occurs.
Use it like this.
body: AnimatedBuilder(
animation: _controller,
child : CounterWidget(
counter: counter,
),
builder: (_, child) => Transform(
alignment: Alignment.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.001)
..rotateY(360 * _controller.value * (pi / 180.0)),
child: child),
),
We use the subproperties provided by AnimatedBuilder, which allow us to cache widgets to reuse them in animations. We do this because the widget doesn't change, the only thing it does is rotate, but for that we have Transform widgets.
More information:
2. Use const whenever possible
class CustomWidget extends StatelessWidget {
const CustomWidget(); @override
Widget build(BuildContext context) {
...
}
}
- When building widgets, or using flutter widgets. This helps flutter only rebuild widgets that should be updated. The widgets do not change when setState is called. It will prevent widgets from rebuilding.
- You can save CPU cycles and use them with const constructors.
3. Use nil instead of const Container()
// good
text != null ? Text(text) : const Container()
// Better
text != null ? Text(text) : const SizedBox()
// BEST
text != null ? Text(text) : nil
or
if (text != null) Text(text)
Add a simple widget to the widgets tree with minimal performance impact when you don't want to display anything.
4. Use keys to improve Flutter performance
// FROM
return value
? const SizedBox()
: const Placeholder(),
// TO
return value
? const SizedBox(key: ValueKey('SizedBox'))
: const Placeholder(key: ValueKey('Placeholder')),
----------------------------------------------
// FROM
final inner = SizedBox();
return value ? SizedBox(child: inner) : inner,
// TO
final global = GlobalKey();
final inner = SizedBox(key: global);
return value ? SizedBox(child: inner) : inner,
This provides better performance by using the flutter of keys to better identify widgets.
5. Optimize memory when using image ListView
ListView.builder(
...
addAutomaticKeepAlives: false (true by default)
addRepaintBoundaries: false (true by default)
);
ListView can't kill it's children because they are not visible on the screen. If children have high-resolution images, it will consume a lot of memory.
Doing these options wrong can result in using more GPU and CPU work, but it can fix our memory issues and you'll get a very high performance view with no apparent issues.
Flutter Memory Optimization Series
Almost everything in Flutter is optimized and enhanced by default, as you probably already know, thanks to the Flutter team at ..
https://medium.com/flutter-community/flutter-memory-optimization-series-8c4a73f3ea81
6. Follow the Dart style
Identifiers: : There are three styles of identifiers
- UpperCamelCase names capitalize the first letter of each word, including the first.
- lowerCamelCase capitalizes the first letter of every word, except the first letter is always lowercase, even if it's an acronym.
- lowercase*with_underscores Names use only lowercase letters, even for acronyms, and words that use \_.
Use UpperCamelCase to name types
Classes, enumerations, typedefs, and type parameters should capitalize the first letter of each word (including the first word) and use no delimiters.
good
class SliderMenu { ... }
class HttpRequest { ... }
typedef Predicate<T> = bool Function(T value);
const foo = Foo();
@foo
class C { ... }
Use the following named libraries, packages, directories, and source files
Good
library peg_parser.source_scanner;
import 'file_system.dart';
import 'slider_menu.dart';
Bad
library pegparser.SourceScanner;
import 'file-system.dart';
import 'SliderMenu.dart';
For more styles, check out Dart styles:
An amazingly important part of good code is good style. Consistent naming, ordering and formatting helps code..
https://dart.dev/guides/language/effective-dart/style
For more rules, check out the Dart connector:
https://dart-lang.github.io/linter/lints/index.html
7. Use MediaQuery/LayoutBuilder only when needed
8. Use async/await instead of then functions
9. Use operators effectively
var car = van == null ? bus : audi; // Old pattern
var car = audi ?? bus; // New pattern
var car = van == null ? null : audi.bus; // Old pattern
var car = audi?.bus; // New pattern
(item as Car).name = 'Mustang'; // Old pattern
if (item is Car) item.name = 'Mustang'; // New pattern
10. Using String Template Interpolation
// Inappropriate
var discountText = 'Hello, ' + name + '! You have won a brand new ' + brand.name + 'voucher! Please enter your email to redeem. The offer expires within ' + timeRemaining.toString() ' minutes.';
// Appropriate
var discountText = 'Hello, $name! You have won a brand new ${brand.name} voucher! Please enter your email to redeem. The offer expires within ${timeRemaining} minutes.';
11. Use for/while instead of foreach/map
You can check the comparison of loops in this article
Comparing Dart's coils — which is the fastest?
Dart, the language in which Flutter apps are written, has many different loops that can loop through a list or run some ..
https://itnext.io/comparing-darts-loops-which-is-the-fastest-731a03ad42a2
12. Display your images and icons precisely
precacheImage(AssetImage(imagePath), context);For SVGs
you need flutter_svg package.precachePicture(
ExactAssetPicture(
SvgPicture.svgStringDecoderBuilder,iconPath),context,
);
13. Use SKSL Warmup
flutter run --profile --cache-sksl --purge-persistent-cache
flutter build apk --cache-sksl --purge-persistent-cache
If an application has clean animations on the first run, and then becomes smooth for the same animations, it is likely due to delays in shader compilation.
14. Consider RepaintBoundary widgets
Flutter widgets are associated with render objects. Render objects have a method called paint that performs painting. However, the paint method can be called even if the associated widget instance does not change. This is because Flutter may perform a repaint on other render objects in the same layer if one of them is marked dirty. When a render object needs to be repainted via RenderObject.markneedspaint, it tells its nearest ancestor to repaint. Ancestors do the same for their ancestors, possibly up to the root RenderObject. When a render object's paint method is triggered, all its child render objects in the same layer will be repainted.
In some cases, when a render object needs to be repainted, other render objects in the same layer do not need to be repainted because what they render remains the same. In other words, it would be better if we could only repaint certain render objects. Using RepaintBoundary is useful to limit the spread of markneedspain and paintChild through the render tree. RepaintBoundary decouples ancestor renderers from descendant renderers. Therefore, only subtrees whose content has changed can be redrawn. Using RepaintBoundary can significantly improve the performance of your application, especially if subtrees that don't need to be repainted require a lot of repainting work.
15. Use named constructors if possible
// 例如
Listview → Listview.builder
16. Appropriate processing of data
Unnecessary memory usage kills data silently inside the app, so don't forget to dispose of your data
Some packages provide autorelease support for their classes
If you don't know what a Flutter hook is and how to use it
Remove all kinds of boilerplate code with Flutter hooks
Don't you think, it's time to close StatefulWidget and use flutter hook to remove boilerplate code
https://iisprey.medium.com/get-rid-of-all-kind-of-boilerplate-code-with-flutter-hooks-2e17eea06ca0
set cacheHeight and cacheWidth to image
You can reduce memory usage this way
17. Don't use references in List Maps
don't use it like this
List a = [1,2,3,4];
List b;
b = a;
a.remove(1);
print(a); // [2,3,4]
print(b); // [2,3,4]
For this reason, whenever you try to call any method of list a, list b is automatically called.
For example a.remove (some) ; will also remove the item from list b;
use it like this
List a = [1,2,3,4];
List b;
b = jsonDecode(jsonEncode(a));
a.remove(1);
print(a); // [2,3,4]
print(b); // [1,2,3,4]
end
I hope this gave you some insight to improve the performance of your Flutter app. Happy coding!
© Cat Brother
- WeChat ducafecat
- Blogducafecat.tech
- github
- bilibili
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。