• United States+1
  • United Kingdom+44
  • Afghanistan (‫افغانستان‬‎)+93
  • Albania (Shqipëri)+355
  • Algeria (‫الجزائر‬‎)+213
  • American Samoa+1684
  • Andorra+376
  • Angola+244
  • Anguilla+1264
  • Antigua and Barbuda+1268
  • Argentina+54
  • Armenia (Հայաստան)+374
  • Aruba+297
  • Australia+61
  • Austria (Österreich)+43
  • Azerbaijan (Azərbaycan)+994
  • Bahamas+1242
  • Bahrain (‫البحرين‬‎)+973
  • Bangladesh (বাংলাদেশ)+880
  • Barbados+1246
  • Belarus (Беларусь)+375
  • Belgium (België)+32
  • Belize+501
  • Benin (Bénin)+229
  • Bermuda+1441
  • Bhutan (འབྲུག)+975
  • Bolivia+591
  • Bosnia and Herzegovina (Босна и Херцеговина)+387
  • Botswana+267
  • Brazil (Brasil)+55
  • British Indian Ocean Territory+246
  • British Virgin Islands+1284
  • Brunei+673
  • Bulgaria (България)+359
  • Burkina Faso+226
  • Burundi (Uburundi)+257
  • Cambodia (កម្ពុជា)+855
  • Cameroon (Cameroun)+237
  • Canada+1
  • Cape Verde (Kabu Verdi)+238
  • Caribbean Netherlands+599
  • Cayman Islands+1345
  • Central African Republic (République centrafricaine)+236
  • Chad (Tchad)+235
  • Chile+56
  • China (中国)+86
  • Christmas Island+61
  • Cocos (Keeling) Islands+61
  • Colombia+57
  • Comoros (‫جزر القمر‬‎)+269
  • Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)+243
  • Congo (Republic) (Congo-Brazzaville)+242
  • Cook Islands+682
  • Costa Rica+506
  • Côte d’Ivoire+225
  • Croatia (Hrvatska)+385
  • Cuba+53
  • Curaçao+599
  • Cyprus (Κύπρος)+357
  • Czech Republic (Česká republika)+420
  • Denmark (Danmark)+45
  • Djibouti+253
  • Dominica+1767
  • Dominican Republic (República Dominicana)+1
  • Ecuador+593
  • Egypt (‫مصر‬‎)+20
  • El Salvador+503
  • Equatorial Guinea (Guinea Ecuatorial)+240
  • Eritrea+291
  • Estonia (Eesti)+372
  • Ethiopia+251
  • Falkland Islands (Islas Malvinas)+500
  • Faroe Islands (Føroyar)+298
  • Fiji+679
  • Finland (Suomi)+358
  • France+33
  • French Guiana (Guyane française)+594
  • French Polynesia (Polynésie française)+689
  • Gabon+241
  • Gambia+220
  • Georgia (საქართველო)+995
  • Germany (Deutschland)+49
  • Ghana (Gaana)+233
  • Gibraltar+350
  • Greece (Ελλάδα)+30
  • Greenland (Kalaallit Nunaat)+299
  • Grenada+1473
  • Guadeloupe+590
  • Guam+1671
  • Guatemala+502
  • Guernsey+44
  • Guinea (Guinée)+224
  • Guinea-Bissau (Guiné Bissau)+245
  • Guyana+592
  • Haiti+509
  • Honduras+504
  • Hong Kong (香港)+852
  • Hungary (Magyarország)+36
  • Iceland (Ísland)+354
  • India (भारत)+91
  • Indonesia+62
  • Iran (‫ایران‬‎)+98
  • Iraq (‫العراق‬‎)+964
  • Ireland+353
  • Isle of Man+44
  • Israel (‫ישראל‬‎)+972
  • Italy (Italia)+39
  • Jamaica+1876
  • Japan (日本)+81
  • Jersey+44
  • Jordan (‫الأردن‬‎)+962
  • Kazakhstan (Казахстан)+7
  • Kenya+254
  • Kiribati+686
  • Kosovo+383
  • Kuwait (‫الكويت‬‎)+965
  • Kyrgyzstan (Кыргызстан)+996
  • Laos (ລາວ)+856
  • Latvia (Latvija)+371
  • Lebanon (‫لبنان‬‎)+961
  • Lesotho+266
  • Liberia+231
  • Libya (‫ليبيا‬‎)+218
  • Liechtenstein+423
  • Lithuania (Lietuva)+370
  • Luxembourg+352
  • Macau (澳門)+853
  • Macedonia (FYROM) (Македонија)+389
  • Madagascar (Madagasikara)+261
  • Malawi+265
  • Malaysia+60
  • Maldives+960
  • Mali+223
  • Malta+356
  • Marshall Islands+692
  • Martinique+596
  • Mauritania (‫موريتانيا‬‎)+222
  • Mauritius (Moris)+230
  • Mayotte+262
  • Mexico (México)+52
  • Micronesia+691
  • Moldova (Republica Moldova)+373
  • Monaco+377
  • Mongolia (Монгол)+976
  • Montenegro (Crna Gora)+382
  • Montserrat+1664
  • Morocco (‫المغرب‬‎)+212
  • Mozambique (Moçambique)+258
  • Myanmar (Burma) (မြန်မာ)+95
  • Namibia (Namibië)+264
  • Nauru+674
  • Nepal (नेपाल)+977
  • Netherlands (Nederland)+31
  • New Caledonia (Nouvelle-Calédonie)+687
  • New Zealand+64
  • Nicaragua+505
  • Niger (Nijar)+227
  • Nigeria+234
  • Niue+683
  • Norfolk Island+672
  • North Korea (조선 민주주의 인민 공화국)+850
  • Northern Mariana Islands+1670
  • Norway (Norge)+47
  • Oman (‫عُمان‬‎)+968
  • Pakistan (‫پاکستان‬‎)+92
  • Palau+680
  • Palestine (‫فلسطين‬‎)+970
  • Panama (Panamá)+507
  • Papua New Guinea+675
  • Paraguay+595
  • Peru (Perú)+51
  • Philippines+63
  • Poland (Polska)+48
  • Portugal+351
  • Puerto Rico+1
  • Qatar (‫قطر‬‎)+974
  • Réunion (La Réunion)+262
  • Romania (România)+40
  • Russia (Россия)+7
  • Rwanda+250
  • Saint Barthélemy (Saint-Barthélemy)+590
  • Saint Helena+290
  • Saint Kitts and Nevis+1869
  • Saint Lucia+1758
  • Saint Martin (Saint-Martin (partie française))+590
  • Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)+508
  • Saint Vincent and the Grenadines+1784
  • Samoa+685
  • San Marino+378
  • São Tomé and Príncipe (São Tomé e Príncipe)+239
  • Saudi Arabia (‫المملكة العربية السعودية‬‎)+966
  • Senegal (Sénégal)+221
  • Serbia (Србија)+381
  • Seychelles+248
  • Sierra Leone+232
  • Singapore+65
  • Sint Maarten+1721
  • Slovakia (Slovensko)+421
  • Slovenia (Slovenija)+386
  • Solomon Islands+677
  • Somalia (Soomaaliya)+252
  • South Africa+27
  • South Korea (대한민국)+82
  • South Sudan (‫جنوب السودان‬‎)+211
  • Spain (España)+34
  • Sri Lanka (ශ්‍රී ලංකාව)+94
  • Sudan (‫السودان‬‎)+249
  • Suriname+597
  • Svalbard and Jan Mayen+47
  • Swaziland+268
  • Sweden (Sverige)+46
  • Switzerland (Schweiz)+41
  • Syria (‫سوريا‬‎)+963
  • Taiwan (台灣)+886
  • Tajikistan+992
  • Tanzania+255
  • Thailand (ไทย)+66
  • Timor-Leste+670
  • Togo+228
  • Tokelau+690
  • Tonga+676
  • Trinidad and Tobago+1868
  • Tunisia (‫تونس‬‎)+216
  • Turkey (Türkiye)+90
  • Turkmenistan+993
  • Turks and Caicos Islands+1649
  • Tuvalu+688
  • U.S. Virgin Islands+1340
  • Uganda+256
  • Ukraine (Україна)+380
  • United Arab Emirates (‫الإمارات العربية المتحدة‬‎)+971
  • United Kingdom+44
  • United States+1
  • Uruguay+598
  • Uzbekistan (Oʻzbekiston)+998
  • Vanuatu+678
  • Vatican City (Città del Vaticano)+39
  • Venezuela+58
  • Vietnam (Việt Nam)+84
  • Wallis and Futuna+681
  • Western Sahara (‫الصحراء الغربية‬‎)+212
  • Yemen (‫اليمن‬‎)+967
  • Zambia+260
  • Zimbabwe+263
  • Åland Islands+358
Thanks! We'll be in touch in the next 12 hours
Oops! Something went wrong while submitting the form.

How to build High-Performance Flutter Apps using Streams

Performance is a significant factor for any mobile app, and multiple factors like architecture, logic, memory management, etc. cause low performance. When we develop an app in Flutter, the initial performance results are very good, but as development progresses, the negative effects of a bad codebase start showing up.  This blog is aimed at using an architecture that improves Flutter app performance. We will briefly touch base on the following points:

1. What is High-Performance Architecture?

1.1. Framework

1.2. Motivation

1.3. Implementation

2. Sample Project

3. Additional benefits

4. Conclusion

1. What is High-Performance Architecture?

This Architecture uses streams instead of the variable-based state management approach. Streams are the most preferred approach for scenarios in which an app needs data in real-time. Even with these benefits, why are streams not the first choice for developers? One of the reasons is that streams are considered difficult and complicated, but that reputation is slightly overstated. 
Dart is a programming language designed to have a reactive style system, i.e., architecture, with observable streams, as quoted by Flutter’s Director of Engineering, Eric Seidel in this podcast. [Note: The podcast’s audio is removed but an important part which is related to this architecture can be heard here in Zaiste’s youtube video.] 

1.1. Framework: 

  Figure 01

As shown in figure 01, we have 3 main components:

  • Supervisor: The Supervisor wraps the complete application, and is the Supervise responsible for creating the singleton of all managers as well as providing this manager's singleton to the required screen.
  • Managers: Each Manager has its own initialized streams that any screen can access by accessing the respective singleton. These streams can hold data that we can use anywhere in the application. Plus, as we are using streams, any update in this data will be reflected everywhere at the same time.
  • Screens: Screens will be on the receiver end of this project. Each screen uses local streams for its operations, and if global action is required, then accesses streams from managers using a singleton.

1.2. Motivation:

Zaiste proposed an idea in 2019 and created a plugin for such architecture. He named it “Sprinkel Architecture” and his plugin is called sprinkle which made our development easy to a certain level. But as of today, his plugin does not support new null safety features introduced in Dart 2.12.0. You can check more about his implementation here and can try his given sample with following command:

flutter run -–no-sound-null-safety
view raw .sh hosted with ❤ by GitHub

1.3. Implementation:

We will be using the get plugin and rxdart plugins in combination to create our high performance architecture.

The Rxdart plugin will handle the stream creation and manipulation, whereas the get plugin can help us in dependency injection, route management, as well as state management.

2. Sample Project:

We will create a sample project to understand how to implement this architecture.

2.1. Create a project using following command:

flutter create sprinkle_architecture
view raw .sh hosted with ❤ by GitHub

2.2. Add these under dependencies of pubspec.yaml (and run command flutter pub get):

get: ^4.6.5
Rxdart: ^0.27.4
view raw .yaml hosted with ❤ by GitHub

2.3. Create 3 directories, constants, managers, and views, inside the lib directory:

2.4. First, we will start with a manager who will have streams & will increment the counter. Create dart file with name counter_manager.dart under managers directory:

import 'package:get/get.dart';
class CounterManager extends GetLifeCycle {
final RxInt count = RxInt(0);
int get getCounter => count.value;
void increment() => count.value = count.value + 1;
}
view raw .dart hosted with ❤ by GitHub

2.5. With this, we have a working manager. Next, we’ll create a Supervisor who will create a singleton of all available managers. In our case, we’ll create a singleton of only one manager. Create a supervisor.dart file in the lib directory:

import 'package:get/get.dart';
import 'package:sprinkle_architecture/managers/counter_manager.dart';
abstract class Supervisor {
static Future<void> init() async {
_initManagers();
}
static void _initManagers() {
Get.lazyPut<CounterManager>(() => CounterManager());
}
}
view raw .dart hosted with ❤ by GitHub

2.6. This application only has 1 screen, but it is a good practice to create constants related to routing, so let’s add route details. Create a dart file route_paths.dart:

abstract class RoutePaths {
static const String counterPage = '/';
}
view raw .dart hosted with ❤ by GitHub

2.7. And route_pages.dart under constants directory:

import 'package:get/get.dart';
import 'package:sprinkle_architecture_exp/constants/route_paths.dart';
import 'package:sprinkle_architecture_exp/views/counter_page.dart';
abstract class RoutePages {
static final List<GetPage<dynamic>> pages = <GetPage<dynamic>>[
GetPage<void>(
name: RoutePaths.counterPage,
page: () => const CounterPage(title: 'Flutter Demo Home Page'),
binding: CounterPageBindings(),
),
];
}
class CounterPageBindings extends Bindings {
@override
void dependencies() => Get.lazyPut<CounterManager>(() => CounterManager());
}
view raw .dart hosted with ❤ by GitHub

2.8. Now, we have a routing constant that we can use. But do not have a CounterPage Class. But before creating this class, let’s update our main file:

import 'package:flutter/material.dart';
import 'package:get/get_navigation/src/root/get_material_app.dart';
import 'package:sprinkle_architecture_exp/constants/route_pages.dart';
import 'package:sprinkle_architecture_exp/constants/route_paths.dart';
import 'package:sprinkle_architecture_exp/supervisor.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
Supervisor.init();
runApp(
GetMaterialApp(
initialRoute: RoutePaths.counterPage,
getPages: RoutePages.pages,
),
);
}
view raw .dart hosted with ❤ by GitHub

2.9. Finally, add the file counter_page_controller.dart:

import 'package:get/get.dart';
import 'package:sprinkle_architecture_exp/managers/counter_manager.dart';
class CounterPageController extends GetxController {
final CounterManager manager = Get.find();
}
view raw .dart hosted with ❤ by GitHub

2.10. As well as our landing page  counter_page.dart:

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:sprinkle_architecture_exp_2/views/counter_page_controller.dart';
class CounterPage extends GetWidget<CounterPageController> {
const CounterPage({Key? key, required this.title}) : super(key: key);
final String title;
CounterPageController get c => Get.put(CounterPageController());
@override
Widget build(BuildContext context) {
return Obx(() {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text('${c.manager.getCounter}',
style: Theme.of(context).textTheme.headline4),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: c.manager.increment,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
});
}
}
view raw .dart hosted with ❤ by GitHub

2.11. The get plugin allows us to add 1 controller per screen by using the GetxController class. In this controller, we can do operations whose scope is limited to our screen. Here, CounterPageController provides CounterPage the singleton on CounterManger.

If everything is done as per the above commands, we will end up with the following tree structure:

2.12. Now we can test our project by running the following command:

flutter run
view raw .sh hosted with ❤ by GitHub

3. Additional Benefits:

3.1. Self Aware UI:

As all managers in our application are using streams to share data, whenever a screen changes managers’ data, the second screens with dependency on that data also update themselves in real-time. This will happen because of the listen() property of streams. 

3.2. Modularization:

We have separate managers for handling REST APIs, preferences, appStateInfo, etc. So, the modularization happens automatically. Plus UI logic gets separate from business logic as we are using getXController provided by the get plugin

3.3. Small rebuild footprint:

By default, Flutter rebuilds the whole widget tree for updating the UI but with the get and rxdart plugins, only the dependent widget refreshes itself.

4. Conclusion

We can achieve good performance of a Flutter app with an appropriate architecture as discussed in this blog. 

Get the latest engineering blogs delivered straight to your inbox.
No spam. Only expert insights.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings

How to build High-Performance Flutter Apps using Streams

Performance is a significant factor for any mobile app, and multiple factors like architecture, logic, memory management, etc. cause low performance. When we develop an app in Flutter, the initial performance results are very good, but as development progresses, the negative effects of a bad codebase start showing up.  This blog is aimed at using an architecture that improves Flutter app performance. We will briefly touch base on the following points:

1. What is High-Performance Architecture?

1.1. Framework

1.2. Motivation

1.3. Implementation

2. Sample Project

3. Additional benefits

4. Conclusion

1. What is High-Performance Architecture?

This Architecture uses streams instead of the variable-based state management approach. Streams are the most preferred approach for scenarios in which an app needs data in real-time. Even with these benefits, why are streams not the first choice for developers? One of the reasons is that streams are considered difficult and complicated, but that reputation is slightly overstated. 
Dart is a programming language designed to have a reactive style system, i.e., architecture, with observable streams, as quoted by Flutter’s Director of Engineering, Eric Seidel in this podcast. [Note: The podcast’s audio is removed but an important part which is related to this architecture can be heard here in Zaiste’s youtube video.] 

1.1. Framework: 

  Figure 01

As shown in figure 01, we have 3 main components:

  • Supervisor: The Supervisor wraps the complete application, and is the Supervise responsible for creating the singleton of all managers as well as providing this manager's singleton to the required screen.
  • Managers: Each Manager has its own initialized streams that any screen can access by accessing the respective singleton. These streams can hold data that we can use anywhere in the application. Plus, as we are using streams, any update in this data will be reflected everywhere at the same time.
  • Screens: Screens will be on the receiver end of this project. Each screen uses local streams for its operations, and if global action is required, then accesses streams from managers using a singleton.

1.2. Motivation:

Zaiste proposed an idea in 2019 and created a plugin for such architecture. He named it “Sprinkel Architecture” and his plugin is called sprinkle which made our development easy to a certain level. But as of today, his plugin does not support new null safety features introduced in Dart 2.12.0. You can check more about his implementation here and can try his given sample with following command:

flutter run -–no-sound-null-safety
view raw .sh hosted with ❤ by GitHub

1.3. Implementation:

We will be using the get plugin and rxdart plugins in combination to create our high performance architecture.

The Rxdart plugin will handle the stream creation and manipulation, whereas the get plugin can help us in dependency injection, route management, as well as state management.

2. Sample Project:

We will create a sample project to understand how to implement this architecture.

2.1. Create a project using following command:

flutter create sprinkle_architecture
view raw .sh hosted with ❤ by GitHub

2.2. Add these under dependencies of pubspec.yaml (and run command flutter pub get):

get: ^4.6.5
Rxdart: ^0.27.4
view raw .yaml hosted with ❤ by GitHub

2.3. Create 3 directories, constants, managers, and views, inside the lib directory:

2.4. First, we will start with a manager who will have streams & will increment the counter. Create dart file with name counter_manager.dart under managers directory:

import 'package:get/get.dart';
class CounterManager extends GetLifeCycle {
final RxInt count = RxInt(0);
int get getCounter => count.value;
void increment() => count.value = count.value + 1;
}
view raw .dart hosted with ❤ by GitHub

2.5. With this, we have a working manager. Next, we’ll create a Supervisor who will create a singleton of all available managers. In our case, we’ll create a singleton of only one manager. Create a supervisor.dart file in the lib directory:

import 'package:get/get.dart';
import 'package:sprinkle_architecture/managers/counter_manager.dart';
abstract class Supervisor {
static Future<void> init() async {
_initManagers();
}
static void _initManagers() {
Get.lazyPut<CounterManager>(() => CounterManager());
}
}
view raw .dart hosted with ❤ by GitHub

2.6. This application only has 1 screen, but it is a good practice to create constants related to routing, so let’s add route details. Create a dart file route_paths.dart:

abstract class RoutePaths {
static const String counterPage = '/';
}
view raw .dart hosted with ❤ by GitHub

2.7. And route_pages.dart under constants directory:

import 'package:get/get.dart';
import 'package:sprinkle_architecture_exp/constants/route_paths.dart';
import 'package:sprinkle_architecture_exp/views/counter_page.dart';
abstract class RoutePages {
static final List<GetPage<dynamic>> pages = <GetPage<dynamic>>[
GetPage<void>(
name: RoutePaths.counterPage,
page: () => const CounterPage(title: 'Flutter Demo Home Page'),
binding: CounterPageBindings(),
),
];
}
class CounterPageBindings extends Bindings {
@override
void dependencies() => Get.lazyPut<CounterManager>(() => CounterManager());
}
view raw .dart hosted with ❤ by GitHub

2.8. Now, we have a routing constant that we can use. But do not have a CounterPage Class. But before creating this class, let’s update our main file:

import 'package:flutter/material.dart';
import 'package:get/get_navigation/src/root/get_material_app.dart';
import 'package:sprinkle_architecture_exp/constants/route_pages.dart';
import 'package:sprinkle_architecture_exp/constants/route_paths.dart';
import 'package:sprinkle_architecture_exp/supervisor.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
Supervisor.init();
runApp(
GetMaterialApp(
initialRoute: RoutePaths.counterPage,
getPages: RoutePages.pages,
),
);
}
view raw .dart hosted with ❤ by GitHub

2.9. Finally, add the file counter_page_controller.dart:

import 'package:get/get.dart';
import 'package:sprinkle_architecture_exp/managers/counter_manager.dart';
class CounterPageController extends GetxController {
final CounterManager manager = Get.find();
}
view raw .dart hosted with ❤ by GitHub

2.10. As well as our landing page  counter_page.dart:

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:sprinkle_architecture_exp_2/views/counter_page_controller.dart';
class CounterPage extends GetWidget<CounterPageController> {
const CounterPage({Key? key, required this.title}) : super(key: key);
final String title;
CounterPageController get c => Get.put(CounterPageController());
@override
Widget build(BuildContext context) {
return Obx(() {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text('${c.manager.getCounter}',
style: Theme.of(context).textTheme.headline4),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: c.manager.increment,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
});
}
}
view raw .dart hosted with ❤ by GitHub

2.11. The get plugin allows us to add 1 controller per screen by using the GetxController class. In this controller, we can do operations whose scope is limited to our screen. Here, CounterPageController provides CounterPage the singleton on CounterManger.

If everything is done as per the above commands, we will end up with the following tree structure:

2.12. Now we can test our project by running the following command:

flutter run
view raw .sh hosted with ❤ by GitHub

3. Additional Benefits:

3.1. Self Aware UI:

As all managers in our application are using streams to share data, whenever a screen changes managers’ data, the second screens with dependency on that data also update themselves in real-time. This will happen because of the listen() property of streams. 

3.2. Modularization:

We have separate managers for handling REST APIs, preferences, appStateInfo, etc. So, the modularization happens automatically. Plus UI logic gets separate from business logic as we are using getXController provided by the get plugin

3.3. Small rebuild footprint:

By default, Flutter rebuilds the whole widget tree for updating the UI but with the get and rxdart plugins, only the dependent widget refreshes itself.

4. Conclusion

We can achieve good performance of a Flutter app with an appropriate architecture as discussed in this blog. 

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings