DEV Community

Cover image for Flutter Performance Optimization In 2023
Kuldeep Tarapara
Kuldeep Tarapara

Posted on • Originally published at flutteragency.com

Flutter Performance Optimization In 2023

As you may already be aware, almost everything in Flutter is optimized and enhanced by default. The Flutter developer has also given us access to beneficial dev tools that enable us to optimize our apps more effectively. However, we won’t discuss these tools or how to use them; instead, we’ll discuss how to write code that deals with memory more effectively and carefully.

1. Split large widgets into smaller widgets

There are numerous nested widgets and other tiny widgets inside a main widget. It is advisable to divide the vast widgets into manageable smaller widgets. It is also a good practice to separate the piece of code into different widgets when we develop a custom widget (such as a custom button) that we may use in multiple locations. It enables us to consolidate the management of Flutter widget modifications and remove additional lines of code.

Example of a nested build method

class _MyStatefulWidgetState extends State<mystatefulwidget> {
 int _counter = 0;
 @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Text('Counter: $_counter'),
        Container(
          child: Column(
            children: [
              Text('Hello'),
Row(
                children: [
                  Text('there'),
                  Text('world!'),
                ],
              ),
            ],
          ),
        ),
      ],
    );
  }
     }
</mystatefulwidget>

Enter fullscreen mode Exit fullscreen mode

Example of a nested build method – split into a separate method

class _MyStatefulWidgetState extends State<mystatefulwidget> {
  int _counter = 0;
  Widget _buildNonsenseWidget() {
    return Container(
      child: Column(
        children: [
          Text('Hello'),
          Row(
            children: [
              Text('there'),
              Text('world!'),
            ],
          ),
        ],
      ),
    );
  }
  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Text('Counter: $_counter'),
        // The deeply nesting widget is now refactored into a
        //different method, and we have a cleaner build method. Yay!
        _buildNonsenseWidget(),
      ],
    );
  }
}
</mystatefulwidget>
Enter fullscreen mode Exit fullscreen mode

2. Use the const keyword

When creating your widgets or using Flutter widgets, use const constructors whenever possible. It allows Flutter to rebuild only the widgets that need to be updated.

So if you have a Stateful widget and you are using setState((){}) to update that widget and you have widgets like:

class _MyWidgetState extends State<mywidget> {
  String title = "Title";
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Column(
        children: <widget>[
          const Text("Text 1"),
          const Padding(
            padding: const EdgeInsets.all(8.0),
            child: const Text("Another Text widget"),
          ),
          const Text("Text 3"),
        ],
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () {
          setState(() => title = 'New Title');
        },
      ),
    );
  }
}
</widget></mywidget>
Enter fullscreen mode Exit fullscreen mode

If you run this code and press the floating action button in Flutter, it won’t rebuild all the widgets defined as const.

3. Avoid using opacity as much as possible

The Opacity widget requires the widget to be rebuilt after each frame, which may impact Flutter’s performance, especially when dealing with animation. To save resources, apply opacity directly to an image rather than using an Opacity widget.

Opacity(
  opacity: _visible ? 1.0: 0.0,
  child: Text("Text!"),
)
Enter fullscreen mode Exit fullscreen mode

Although this works, keeping your widget hidden on the screen is costly. Instead, try rebuilding your widget, so it does not require the Text widget. If you want the widget to take up space on the screen, use the Visibility widget, which is more efficient than opacity because it only has two states: visible/invisible (it does not support fractional opacity values).

4. Use for/while instead of foreach/map

For loops

num element= 0;
var length = list.length;
for (var i = 0; i < length; i++) {
element= pow(list[i], 3);
}

While loops

var count = 0;
num element= 0;
var length = list.length;
while (count < length) {
element= pow(list[count], 3);
Count++;
}

For…in loop

num element= 0;
for (var value in list) {
element= pow(value, 3);
}

For Each method

num element= 0;
list.forEach((value) {
element= pow(value, 3);
});

Map method

num eachElement = 0;
list.map((e) {
eachElement = pow(e, 3);
}).toList();

5. Avoid rebuilding unnecessary widgets inside Animated Builder

Adding animation to our widgets is a frequently requested feature. In most cases, we add a listener to our Animation Controller and then call set State from there. However, it is only sometimes a good idea.

We will create a screen with a widget to the centre that shows the current count and that when pressing the fav button, the widget rotates 360.

class _MyHomePageState extends State<myhomepage>
   with SingleTickerProviderStateMixin {
 late AnimationController _controller;
 int counter = 0;
 void _onPressed() {
   setState(() {
     counter++;
   });
   _controller.forward(from: 0.0);
 }
 @override
 void initState() {
   _controller = AnimationController(
       vsync: this, duration: const Duration(milliseconds: 600));
   super.initState();
 }
 @override
 void dispose() {
   _controller.dispose();
   super.dispose();
 }
 @override
 Widget build(BuildContext context) {
   return Scaffold(
     floatingActionButton: FloatingActionButton(
       onPressed: _onPressed,
       splashColor: Colors.lightBlue.shade200,
       backgroundColor: Colors.blue,
       child: Icon(
         Icons.flip,
         color: Colors.white,
       ),
     ),
     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: LogoWidget(
           counter: counter,
         ),
       ),
     ),
   );
 }
}
class LogoWidget extends StatelessWidget {
 final int counter;
 const LogoWidget({Key? key, required this.counter}) : super(key: key);
 @override
 Widget build(BuildContext context) {
   print(' Rebuilding Widget');
   return Center(
     child: SizedBox(height: 100, width: 100, child: FlutterLogo()),
   );
 }
}
</myhomepage>
Enter fullscreen mode Exit fullscreen mode

Perfect, we already have it. Now let’s add a print in the logowidget to see what happens and try again.

flutter: Rebuilding Widget
flutter: Rebuilding Widget
flutter: Rebuilding Widget
flutter: Rebuilding Widget
……
Enter fullscreen mode Exit fullscreen mode

6. Flutter: Rebuilding Widget

We can see that it is rebuilding our widget while rotating. However, many print statements exist; how can we avoid them? (It’s OK to rebuild only once because of the set State we’re using to refresh the counter).

We use the Animated Builder’s child attribute, which allows us to cache widgets and reuse them in our animation. We do this because that widget will not change; it will only rotate, but we have the Transform widget for that.

@override
  Widget build(BuildContext context) {
   return Scaffold(
     floatingActionButton: FloatingActionButton(
       onPressed: _onPressed,
       splashColor: Colors.lightBlue.shade200,
       backgroundColor: Colors.blue,
       child: Icon(
         Icons.flip,
         color: Colors.white,
       ),
     ),
     body: AnimatedBuilder(
       animation: _controller,
       child: LogoWidget(
         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,
       ),
     ),
   );
 }
}
Enter fullscreen mode Exit fullscreen mode

We get the same visual result, but if we look at the log, we can see that it is no longer rebuilding our LogoWidget (only once because we update the counter), so we have optimized our animation.

7. Do not use helper functions for rendering the user interface

Look for helper functions if you’ve worked with different Cross-platform development frameworks or any other programming language. Helper functions are helpful when developing Flutter apps but not when rendering UI. For example, if parts of a screen in your app repeat, consider creating a stateless widget rather than writing a private helper function to create the widget, which would help to improve Flutter desktop performance.

Conclusion

Flutter performance optimization has always been valuable as the Flutter framework has given tremendous support in native and web app development. It is also compatible with huge visual effects applications.

However, it is vital to have the Flutter performance optimization to ignore unwanted errors and slow the performance. Flutter brings scalability, security, and reliability to your mobile application.

Frequently Asked Questions (FAQs)

1. Is the performance of Flutter good?

Flutter is a promising cross-platform framework in terms of development speed, and there is a reduction in budget. Hence, it is possible to develop MVP applications in Flutter within 3-4 months.

2. How can I optimize my Flutter website?

You can optimize the performance in Flutter apps with tree shaking and deferred loading. It improves the perceived performance with image placeholders and precaching and will disable the navigation transitions. Hence, building the performant Flutter widgets.

3. Why is the Flutter framework better than other frameworks?

Flutter code can run on mobile, desktop, and web platforms. Hence, you do not need to hire developers for each platform. Write the code once in Flutter, and rest assured that the app will work across the other platforms. Thus, it makes the Flutter framework time-consuming and cost-effective.

Top comments (0)