[go: up one dir, main page]

0% found this document useful (0 votes)
57 views40 pages

Guide Flutter For Enterprise by LeanCode

Uploaded by

Pham Van Hai
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
57 views40 pages

Guide Flutter For Enterprise by LeanCode

Uploaded by

Pham Van Hai
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 40

Guide:

Flutter for Enterprise


Build your app the right way
Editorial note Author: Łukasz Kosman

CEO & Co-Founder 



of LeanCode

Flutter has become enterprise ready at 
 However, building the app for an enterprise is a whole
an unprecedented speed from its origins in late 2018. new thing. It takes a different approach to organizing
Highly performant, cross-platform apps coupled 
 the project, the code, the design system, testing
with excellent developers’ experience made 
 practices, and the overall product roadmap.

a compelling case for both business and tech owners


all around the world. As a result, we could have Therefore, we wanted to build this comprehensive
witnessed not only the apps coming from scaleups 
 guide to pave the way for the enterprise teams to build
like Wolt and Delivery Hero but also corporate 

players, even from well-established banking industry their large-scale products based on Flutter. This ebook
like Credit Agricole.

is based on an experience gathered by our team of 30+


Flutter Developers from LeanCode while working 

Based on those cases, we can say for sure that the vast on the corporate applications for Credit Agricole Bank,
majority of guidelines and best practices for Flutter Millennium Bank, and other multinational corporations
were published with smaller apps for startups in mind. which build their core services and products based 

on Flutter.

List of content:
The following chapters will help you to better prepare for building your large-scale apps using Flutter:

1 Flutter at Scale. We start with comprehensive guidance on how to organize a developers’ team. 

Do you wonder what is the optimal structure of code from the long-term perspective? How to ensure 

code ownership? How to maintain the app and its codebase? We will provide a detailed, case-based
argumentation for the most important choices in your future Flutter enterprise project.

2 The Architecture of the Flutter App. We will explain what it takes to build a feature in a large-scale app 

and how to make the right choices from the app’s architecture perspective to make it sustainable 

in the long run.

3 Automated UI Testing. Till recently, this was a missing element in the Flutter universe. With the arrival 

of Patrol, the ultimate UI Testing Framework for Flutter, you are protected from regression bugs 

and can run integrated e2e tests between feature squads in your development team.

4 Add to App approach. In an enterprise environment, it is not always possible to start green-field projects. 

It takes time to substitute the existing app, and business owners cannot withhold the arrival of new features
from the backlog. Therefore, it is sometimes advisable to use an add-to-app approach. Read about the best
practices on how to organize the project, navigation, communication, networking, and other important
elements which will help you to add Flutter to your native apps successfully.

5 Staff Augmentation 2.0. In order to develop large-scale apps in a short manner of time, you often find
yourself in need of scaling up the team. Yet, what you truly want to achieve is to attract the best experts 

who can facilitate the knowledge transfer process to your team while building the Flutter app based 

on a proven experience from other enterprise apps. At LeanCode, we have experts who not only are 

all computer science graduates from the best technical universities in Poland but also have vast 

experience in building more than 60 Flutter apps. We will share the best practices on how to build 

hybrid teams to get the most out of the staff augmentation services.
Table of Contents
Flutter at Scale 4
Feature-Based Mobile Architecture 19
The Role of UI Testing and How Patrol Can Help You Improve Them 23
Flutter Add to App - Overview and Challenges Based on Real-Life Case 27
Building Large Scale Apps with LeanCode - Staff Augmentation 2.0 34
About LeanCode 40
Flutter at Scale Author: Mateusz Wojtczak,

Senior Flutter Developer &


Head of Mobile at LeanCode

Building an enterprise-scale application requires 
 The following is divided into 10 chapters:

a specific approach to organizing the team and the


code they create. After the architecture, organization,
and communication are pivotal to secure yourself 

a smooth path towards the milestone on the roadmap 1 Code ownership
for large-scale projects.

Why building an app for an enterprise is so much 2 Feature-based project structure


different than for a startup or small scaleup?

When we’re talking about large projects, we mean 
 3 Running tasks in monorepo
a project spread across different teams, companies,
and business domains because development is only 

4 Cross-squad communication
a small part of the whole. In small projects, we often
know every developer who’s working with us. There’s
even a huge possibility that a backend dev that serves 5 Navigation
us some API sits next to us the whole time. It’s much
easier not to worry about some issues when we don’t
encounter them. In large-scale apps, we are rarely in 
 6 Localization & Translation Management System
a situation where, while building a powerful contract-
based asynchronous communication with our
7 End-to-end automated testing
backend, we can just go get some coffee with 

the backend team and discuss everything.

8 Strongly-typed contracts
So, in large projects, we just want to help ourselves - 

to deal with multiple obstacles that we cannot directly
resolve - obstacles that are not only related to code. 9 Taming legacy
Let’s see how we can wrap our heads around this level
of complexity not only in terms of code but, most of all,
in communication.

10 Design system

In this study, we would like to share our thoughts 



and experiences from our two-year-long journey of
developing a very large banking mobile application
using Flutter working with 30 Flutter Devs spread
across 15 teams.

Flutter For Enterprise


Code ownership Let’s put some ground rules on how we define code
ownership in a Flutter project of this size.

Let’s talk about code ownership. 




First and foremost, every line of code is owned by 

In a small project done by a single team, there isn’t too a team. Not a single person, a team. Even if the team
much to talk about (or is it?). The team wrote it; 
 consists of a single developer, it is still the team's
they own it and are responsible for it. Although, they responsibility to maintain it. Developers are rarely 

might internally split the responsibility further, it doesn’t self-governing, and there is always someone else
change the external perception - the code is owned by (“business people”) that prioritizes the work.

a team. The reality might be slightly different, but it will


boil down to a variation of this. If the team does not own That’s why a single person should never be
the code, it will deteriorate quite quickly. So let’s responsible for a piece of code. If that happens, 

pretend that this happens every time.

then doing anything with it will require going through 



so many managers with different priorities that it will
However, what if we are talking about a not-so-small never, ever change. Teams are part of the structure 

project? In this case, there were, at the highest point, at a level that gives them enough power to correctly
30+ mobile developers split into 12 business teams prioritize the work around the code and pay for it 

(also known as squads). We might say that this is just 
 by “wasting” hours of work. A single person cannot do
a simple extrapolation - each team owns the code they that.

create. That would be true. Mostly. The problem arises


when we realize that the teams work on a single app This leads us to the fact that, even if a team has 

and single codebase. A. Single. Codebase. 
 a single dev and they switch teams (or leave), 

So, by another extension, if every team creates the the responsibility stays with the team. There rarely 

code in the same codebase, then is every team is a situation where a team gets dismissed or the whole
responsible for basically everything?

staff of a team leaves. That’s why there will always 



be a person that owns that piece of code (even if from 

In that case, no one will be responsible for anything. a business perspective).

There might be places where some people will feel


responsible for themselves, but that will be an And by responsibility, we mean that the code is
exception. If you find a bug, nobody will be willing to fix maintained by the team. If a bug is found in a code
it because that will always be someone else’s problem. owned by the team, their responsibility is to reserve
If you need another feature, you will need to write it time for a fix and do it. They also need to ensure that 

yourself because no one cares.

the code adheres to the always-changing coding


standards. It doesn't, however, mean that only they 

Team ownership works because the team members are allowed to add new code there. If there is a need,
feel responsible for what they create. There is a natural someone else outside of the team can edit the code -
limit to this - if too many people will “feel” responsible but that does not change the one responsible; 

for a piece of work, no one will really feel responsible it’s still the team.

because always someone else will feel more adequate.

And although, I am rather pragmatic when it comes to


We need to follow the limit and put boundaries exactly all the rules, meaning “there can always be
where they are. In a project of this size, these will not exceptions”, this is the one that I think should be
come out naturally. We need to put them and abide by followed with religious devotion. Otherwise, 

the rules explicitly. If we do this right from the start, 
 there will be problems.
it will not feel artificial, and it won’t be questioned.

After all, the rule is natural. And everyone feels like it.
Everyone feels responsible for the work they do, 

but we need not allow a situation when too many
people feel that for the same piece of work.

That is the problem, and that’s why we need to


overcome it by introducing explicit code ownership.

Flutter For Enterprise


So, we know that we need to preserve ownership, but There are real things that are problematic and will bite
how should we define who owns which part of the you from day one.

code? When a whole application is developed by 



a single team (be it a small mobile app or 
 In every project, there always is a “common” folder 

a microservice on the backend), the rule is simple: 
 for things that are used by everyone and a place where
they own everything they create. There will be some the local packages are bootstrapped and combined to
shared code in terms of internal libraries, but these 
 create the final application binary.

will be created and maintained by a different team, 



and we probably won’t see the code at all - we will 
 We really don’t want these kinds of packages because
just consume it. 

they don’t have clear ownership - everyone writes


code there (directly or indirectly), but no one feels
In a big Flutter application that is developed at a rapid responsible for it. We also can’t ditch them completely
pace, that’s not that simple. Effectively everyone works because we need to have some shared ground;
on a single codebase, sometimes with not-so-clean otherwise, we would re-implement the same thing 

business responsibilities. Of course, we could over and over again (which will happen, but with
introduce very clear boundaries and physically common - at a much smaller scale). We also need 

separate the teams, but that would introduce so much an “app” package - the one that brings everything
friction and so much grunt work for the one responsible together. This one is even more important than 

for putting it all together that the development would the first one - we can’t replicate it. We are required to
grind to a halt. We need to find a different way.

have a single entry point to the app. And everyone


needs to contribute to the app.

And, to be honest, the answer is not that far away - 



let’s split the project into packages, the same way we We cannot make an exception for these two. Not
would do when splitting physically, but keep everything abiding by this rule will be far more disastrous than
in the same repository and reference (& compose) following it. This means that we need to find a way.

using just normal path references.

What we found out, the approach that works is not to


This will make the boundaries clean, but the walls won’t find some team that naturally fits to be the owner of this
be so high - you can always look into other teams' kind of code. What works is to create an “artificial”,
codes without any fuss because they are right there, 
 technical team. The team should be responsible for 

a click away. You also compile everything at once, 
 all the things that no one else wants. 

so provided that you enforce that on CI (and you


should!), everything will work more or less at all times. Don’t mistake it for a team of exiles, they have a much
You will also, just by a random chance, test other more important role.

people’s code.

And although it might seem like a bad idea. They don’t


As with everything, this is not a silver bullet. This bring clear business value. They seem like a team that
approach has problems that need to be overcome. 
 no one needs - no business product owner, they don’t
For once, because the walls are not that high, 
 create end-user features, and you can’t easily plan
there is the possibility that someone will modify 
 their work in sprints.

the code behind the owners' back. This is something


that might be a problem when your team does not want
to adhere to rules but is primarily an organizational one
- and I would say that if that happens, it means that

you have other, more critical problems.

Flutter For Enterprise


However, their work is vital. You can’t possibly allow Every small technical bug, every small optimization,
30+ people to work on a single codebase and expect and every small feature that is required to make 

that they will come up with a coherent, maintainable the app work and be maintainable will, in the end,

architecture. That they will never stomp on each be their responsibility. The technical team, 

other’s toes. That they will spontaneously combine although not at all times, will have plenty of work.

everything into a single application. That won’t “just”


happen. There might be natural-born leaders that will Because at the end of the day, someone has to be
do this work because they will feel like they are the responsible. And if no one wants to be responsible,
ones that should do it, but leaving that to chance will the technical team will be.
make the process longer and more painful.

Feature-based project
Every project of this size requires a team of
“architects”, someone that overlooks 
 structure
the development, someone that puts the basics 

Starting the project often starts with us thinking about
in place and puts the ground rules of development. 

how we can structure this one better than 

The team is responsible for the code that no one needs,
the previous one. There are multiple propositions of
and they are responsible for making sure that
structuring code; we have a lot of architectural patterns
everything works together. They put in the ground
that enforce or promote some. There are two main
rules, and they ensure that they are followed. They
approaches - we call them horizontal and vertical
design how to (technically) communicate, and they

structures. You may have heard about layering your
try to satisfy every developer need that is not directly
code. It’s a common thing that people propose to divide
related to the business but needs to be done
code into layers that handle different abstractions.

nevertheless.

The layers often go from the end user (UI layer) through
Keeping up with every developer’s needs can be
some view & application logic until they reach pure
exhausting. And that is not the only responsibility of 

data - that means some domain logic or data contracts.
a technical team. As I said - they are the ones
That’s what we call a horizontal approach.
responsible for the overall architecture, for keeping
everything in proper order. That, although it might not
seem as much, is a lot of work.

Repositories package

AccountsRepository LoansRepository BenefitsRepository

Blocs package

AccountsCubit LoansCubit BenefitsCubit

UI package

AccountsDetailsPage LoansPage BenefitsPage

Flutter For Enterprise


As we can see in the diagram, in this example, 
 The team is most likely divided into business squads
we have a UI layer with some Flutter widgets, 
 that have their responsibilities according to some
an application logic layer with some Blocs/Cubits, 
 business domains. In that way, there are also people 

and a data layer with repositories that represent our in squads that are crucial to proceed but are not even
data sources. While it may not seem so bad, it can next to software development. Ok, so we have our
actually corrupt our project in the long run. Let’s see “Loans Squad Daily Meeting”. We can talk about
what that architecture really means.

business requirements, and as developers, we can


extract business knowledge from other people 

Why do we structure the code? To organize it better. and process it so that we know how to build 

Why organizing? To communicate better.

the software around that.

That’s really something that we can build our code In this domain-driven workflow, let’s come back to
structure on. As it was said in previous chapters, 
 code structure. Let’s try to project our communication
we really need code ownership. So, let’s try to answer model onto that:

this question: Who owns the repository package? 



Who owns the widgets package?

Well, everyone and no one, of course. We could bring


an iconic software aphorism in here - Conway’s Law. 

It states that:


Any organization that designs a system
(defined broadly) will produce a design
whose structure is a copy of 

the organization's communication
structure.

— Melvin E. Conway ”
So, what does it mean for us? It means that no matter
how we structure the code, it would still end up shaped
similarly to the organization. That means we would like
to project our organization’s communication structure
onto our code so that we don’t have to divide our code
artificially; just do it as we do with the rest of our work.

In that case, let’s ask ourselves: how do we


communicate? Is there a “Repository Daily Meeting”?
Is there a “Widgets Daily Meeting”? Not at all.

Flutter For Enterprise


Repositories package Loans package Benefits package

AccountsRepository LoansRepository BenefitsRepository

AccountsCubit LoansCubit BenefitsCubit

AccountsDetailsPage LoansPage BenefitsPage

Now we can see that if the same developers work 
 How can we organize it inside the package? Let’s use
on anything related to loans, then they don’t have to the same assumptions as with package layering. Let’s
work outside their packages. This way, we also try to make a vertical approach inside packages. To do
minimize code conflicts between squads. Our daily that, we need to define what the core unit of our work
work is going to be revolving around those packages is: in our example, it’s a feature. In agile development, 

that are directly owned by our squad. That’s what 
 a developer does a user story enabling users to do
we call a vertical approach, and it allowed us 
 something related to the business. If we want to let
to maintain a large project code structure.

users see their loan agreement documents, then we


have to do everything from UI to data sources because
Also, in this example, we show a 1:1 relation between otherwise, the feature doesn’t make sense. So, we can
squads and packages. Actually, it’s a bit of a different define our features inside a package, and inside every
rule. As it goes with previous code ownership feature, we can split our work into blocs, widgets,
assumptions, we allow multiple packages per squad. pages, data classes, etc. What happens inside 

It’s perfectly normal that there are some larger a feature directory stays there. For other devs, it’s 

business domains that are still strictly related to 
 a black box as long as they don’t need to go in there.
the squad, but devs need another package for that.

For example, “Onboarding Squad” can have their
onboarding customer journey but also a KYC (Know
Your Customer) module that seems to be a separate
thing and can be organized in a separate package. 

The important thing is to remember that one squad 

can own many packages, but every package should
have only one owner.

However, this doesn’t exhaust the subject of code


structure. What about the actual code inside 

the package? It’s also essential to have some
guidelines here. That’s because you want your code
structure to be predictable. When one developer
swaps squads with another, you don’t want them 

to spend weeks studying the code before anything
really happens.

Flutter For Enterprise


Running tasks in mono repo It helps maintain many popular packages like
FlutterFire (Firebase libraries for Flutter), Flame (game
The next thing that’s crucial in large projects is engine), or Flutter Community Plus plugins. However,
simplicity and consistency in doing daily jobs. 
 the most important thing is that you can run any shell
What we mean by that is we don’t want to have trouble script in each package directory, and you can filter
restoring dependencies in our package.

what packages it should consider.

A developer would want a simple way for running tests It’s really helpful because you can define such scripts
just in their packages, running formatting, etc. To run all and filters once, write some simple docs (although
these things in a multi-package environment, we need melos.yaml file is already pretty convenient to read)
a task runner like Make or “*insert your favorite and help new developers on your team be quickly
language*ake”. It would be really convenient if we had 
 onboarded in the environment. Having a task runner
a task runner that is somehow aware of Dart/Flutter also simplifies building a CI/CD pipeline because some
packages and is adjusted to the ecosystem we work in.

tasks are already defined there.

Fortunately, we found that there already are things 
 Last but not least, you can use concurrency for your
for that. One that we have selected is Melos. 
 scripts. But beware of that because your Flutter scripts
It’s officially described as “A tool for managing Dart may fail to run in parallel. In some cases, there will be
projects with multiple packages.”. That’s pretty much locks so that even if you start it concurrently, the tasks
exactly what we need looking at the description. would still have to wait for each other. Also, Melos
command output can end up messy when a lot of
packages are being considered since it doesn’t
Melos provides a couple of scripts itself, such as:
maintain the order - simply speaking, the output is
​Automatic versioning & changelog generation mangled from all the executions.

Automated publishing of packages to pub.dev


Local package linking and installation Still, it’s a great tool, and you probably should use it,
Executing simultaneous commands across especially when working with large Flutter projects.

packages
Listing of local packages & their dependencies.

Flutter For Enterprise


Cross-squad communication Our solution to that is, as an idea, quite simple. 

We divide the communication into two groups -
synchronous when we need some data or we need 

The physical split into (local) packages does not solve to do action “right now”. This covers, for example, 

all the problems. And, unfortunately, they amplify the aforementioned authorization or getting 

others. Life is not simple. Although you might have 
 the accounts list. And asynchronous, where we 

a “clean cut” when it comes to team responsibilities, invert the dependency and expose that some action
you must assume that there will be a point when occurred as an event. The other squad can subscribe
someone will need someone else.

to it and react, updating their data. This covers


updating the account (which is a reaction 

With twelve squads and a banking app, some in the accounts team) to a new transaction 

features depend on data from other squads, 
 (an event raised by the payments squad).

and some invalidate data in others.

We also put some more concrete constraints there. 



You can’t possibly have an app that has 12 completely For example, the synchronous facade should expose 

separate features. Take, for example, the accounts 
 the data as streams (backed by `BehaviorSubject`) 

in a banking app - it is the most essential feature to see 
 so that integration and auto-update (after an event) 

a list of accounts that you own. Every feature depends are easily doable.

on it this way or another. For example, when you see 



a transactions list (another basic feature), you must see Because of that, the data should be automatically
it in the context of an account. On the other hand, 
 provided (when requested), and every failure should be
when a payment is made, it changes the account automatically retried. The error should not, in most
balance, another property of the account.

cases, be visible through a facade because you can’t


react to it sensibly - and if you allow reacting to it, 

As you can see, there always will be dependencies you can easily react too much. Multiple teams will 

between teams. What is worse, the dependencies will show the same error message.

not be unidirectional. There might be cases where two


teams need to communicate with each other in both The other kind of interaction, the one where you don’t
directions. One example is: you need some data 
 need data but you need to do something, is even
(must be synchronous), then you do something 
 simpler - you expose a properly named & self-
and expect someone else to update 
 contained method that does what you need, 

the aforementioned data (might be asynchronous).

the way you need. Making more rules here is really


unnecessary.

But that is just one example, and arguably simpler 



(to maintain, not to develop) one because you can The other kind of facade, the asynchronous one, 

easily invert the control by, e.g., using events, resulting is mostly similar, except it does even less. If you do
in what is effectively uni-directional dependence. 
 some kind of action, you expose an event that
But there are cases where you need access in both describes what occurred. For example, when the user
directions. I personally worked on a squad that makes a payment, you expose a data package that 

provided basic authorization functionality, which 
 tells you what the source and destination accounts are,
was used by arguably every squad. On the other hand, what the title is, and what amount it covers.

when displaying some auth-related configuration, 
 Nothing more is exposed. You also don’t store

I needed the information from accounts. This couldn’t the events - we’re not doing event sourcing or any
be made asynchronous - everything needs to be persistent-event architecture. After all, every event 

provided now. And we have a cyclic dependency that, we publish is meant to update some UI or trigger data
although solvable without too much fuss, is another reload. The heavy lifting is done on the backend.
thing to maintain.

And because of the plethora of options, we need to put


some constraints there so that we can do everything
with ease. And to make it last.

Flutter For Enterprise


Navigation
Another form of cross-squad communication is cross-
squad navigation. In such a huge domain split between
twelve different business squads, navigation to many
pages in the applications will be from a different squad.

For example, from the page presenting details of a bank


account that is owned by the accounts squad, 
 Separation from Flutter
there must be a way to seamlessly navigate to a bank
transfer that is owned by the payments squad. 
 Navigation is a part of business logic, and because of
Such a navigation use case needs to be addressed this, it should be available from the bloc/cubit
while adhering to the code ownership principles and business logic code. However, usually, we avoid
code separation between multiple dart packages. introducing a dependency on Flutter in business code.
This constraint means that we have to trigger 

the navigation on the view side somehow. This is
To achieve those objectives, we implemented custom usually achieved by creating some pseudo-state 

navigation based on the Flutter standard navigation or by introducing an additional stream for navigation
with the following objectives: events.

Separation of navigation targets from the page In our solution, because the navigator state is
implementatio independent of Flutter, we have no issue with passing 

Allowing for passing the context data between it to the business logic, and we can execute navigation
page right from the bloc/cubit.

Separation of a navigator from Flutter

Separation from page


implementation
To allow for navigation between different squads
without exposing the specific page implementation
between pages and squads, the page definition has
been split between the globally visible target 

and package-private builder, which builds the page
based on data from the target.

The targets are similar to intents known from the


Android development world. A target is a class
representing a specific page in the app. It can also Localization & Translation
contain additional context data, which needs to be
provided in order to build the page. For example, 

Management Systems
the target for the bank account details page will 

contain the bank account number of the account 
 The next important thing when developing large
that should be displayed.

Flutter applications is being available to people from


different cultures, locales, and countries. Thus, 

The target is an identifier necessary to execute 
 we need a way to set up localization (often put as l10n).
the navigation; it must be available to all squads that Localization “is the process of adapting a product's
might need to navigate to the page, and thus, 
 translation to a specific country or region”.
the targets are defined in the central navigation
package.

Flutter For Enterprise


It’s part of a larger process called internationalization The problem is that information about what to localize
(or i14n). There are hundreds or thousands of string and how to do it doesn’t come from the development 

values used across the whole app, and they have to be at all. In most cases, it is business knowledge that has
translated according to the current user locale on their been processed by many people in the organization:
device or personal setting within the app. In our case, marketing people, product owners, translators, and
there were over 5500 string values that we needed 
 others. Developers are only one small part of that. This
in two languages for the first release, but we also gives us a reason to think that localization is no longer 

needed the possibility of adding more languages later a development thing. It’s a product thing. Thus,
at a low cost. That’s why you need a localization localization files stored in code repositories should not
solution.

be a single source of truth for that. Going back to code


ownership, localization should also be owned, and that
Flutter already has a preferred solution for l10n. 
 ownership should be transferred to business people.
There are two main packages from the Flutter team:
one called flutter_localizations (for l10n) and another Fortunately, there already are things for that. They 

called intl (for i14n). These two are connected, and you are called Translation Management Systems and
can say that they work together to make your app typically are web apps optimized for localization
available to everyone. The most popular approach is 
 workflows. In our project, we used Phrase as a TMS
to use those packages along with .arb format files that tool because it supports the .arb format and has a lot of
contain “key-value” information about each localized convenient features like comments, activity tracking,
string. roles, and advanced translation workflows.

TMS tools can help you with things like:


Tracking the history of translation terms
Discuss localization through comments
Manage a glossary of business domain-related
terms that could be uncertain for translators
Tagging terms
Importing keys and values from l10n files
Exporting translations to l10n files
Versioning translations (like Git in the development
world).

HELLO!

HOLA!
CZEŚĆ!

Flutter For Enterprise


Another important thing is to remember that 
 As we can see, it’s a specifically designed workflow 


the translation workflow behaves in a different way for working with localization. TMS tools are crucial 


than the development process. Thus, we need to have to have in your project (even if it’s not large) because

tools that know about it. In a large project, we have using them is simple, and a lot of l10n-specific things

business people that make initial localization terms, are taken care of for us.

then it goes to translators (probably more than one)

that also send some translations to native speakers 
 Last but not least, check the trial version of your TMS

to consult and confirm. After that, localizations can be tool first. It’s really important because a lot of TMS tools

accepted by translators or changed again.

have different features and handle some specific file

formats in different ways, so especially with Flutter .arb

That means we need a way to verify some already files, which are not so popular in the l10n world, 


translated terms. It’s hard to visualize this process, so you better be sure that the tool you are going to pay for

let’s take a look at the Phrase workflow diagram: is compatible with your development.

Automatic UI tests

I think we all can agree that UI tests are great. They

can simplify testing tremendously. They can lessen

the number of regressions. They can give you feedback

if you break something right away. They might be hard

to maintain, and you probably need to have a dedicated

team just for writing UI tests, but it’s still worth it.

Until recently, however, there was a problem with UI

Tests and them working within the Flutter app. 


There were some building blocks like Flutter Driver 


to test Flutter parts, and you could use Appium for 


the native parts, but that is cumbersome. It gets even

worse if you have two separate teams: one writing 


the app, the other writing the tests.

Flutter For Enterprise


Therefore at LeanCode, we have created a totally The only solution to that is to make the UI tests 

new approach that will bridge the gap between the a normal part of the SCRUM team. UI tests should
native part of the app and the Flutter interface. probably be a part of the acceptance criteria for each
and every user story. Only then will UI tests 

not cannibalize developer time, and everyone will 

It is called Patrol, and it is an open-source framework want to write them - not only devs & testers but also
for writing UI automation tests in Dart for Flutter apps.
business people. Everyone will have a stake in making
It helps to handle all kinds of permission dialogs them so that they will get created. Since they will be 

between OS and the Flutter app so that you can easily a part of the SCRUM team and will develop
test the scenario where you need to take the code from simultaneously with features, they will change 

an incoming SMS or open push notifications. with them (because most of the time, the changes 

in the app and in tests would be symmetric), and that
will ensure that they will not deteriorate.

In the long run, this is really the only solution if you


have a big, multi-team project where different powers
pull in different directions.

Patrol is the final building block to make


Flutter enterprise-ready. Also, let’s not forget that UI tests are still tests - 

If you want to explore Patrol and UI Testing as such, and should be treated as tests. You should run them
you should read the last chapter of this eBook which with your normal development workflow. I agree 

will allow you to better understand how Patrol works. that they might take too long to run, and it might not be
feasible to run them on every build, but at least 

a minimal viable subset should be run that way. 

And everything else should be run periodically 

Having a tool is one thing. Using it properly 
 (a couple of times a day at least).
is a completely different case. When the QA team 

is separated from the regular development cycle, 

they are working in a cascade mode. Oftentimes
writing a good test requires that the QA specialist
knows the code. Otherwise, doing the procedure 

& pinpointing the code owner takes time, although,
without it, you can’t start writing the tests. Eventually,
the best person for doing the job will be someone 

from the inside - a dev. And dev time is precious.

And that creates a problem:

1 It takes developers’ time,

Which, if not accounted for, might be


2 considered “wasted”,

3 So it is not in line with PO’s goals,

4 Hence they don’t want to do it,


So developers can’t provide necessary
5 things for people doing UI tests,

6 And they waste their time waiting on blockers.

Flutter For Enterprise


Contracts
We all know that somewhere there is a normal JSON
(or XML)-based API. Probably someone will like to call it
a REST API, REST-ful, REST-ish, or just “an'' API. 

Or GraphQL one. That does not really matter. 

No matter what API style you use, it still requires 

a non-negligible amount of work.

First and foremost, you must manually manage it. 



You need to design every endpoint deliberately. 

You need to consider every aspect of the API: will it be 

a problem to compose a request? Do I require some
other request to be done before this one? Or was 

a request done after this one? How do we ensure that
the same data is passed to a couple of different
requests? Or how can we tell that the format of 

the data will be exactly the same across a single “area”
of the API? Although it seems simple, it’s not really 
 So, our solution to that is “strongly typed contracts”. 

that simple: it’s not easy to express that in a structured A concept that is now widely used both in the banking
way.

app and at LeanCode.

Yes, we have OpenAPI that allows us to express that, The idea is simple: since backend devs are the ones
and yes, although manually, we will probably be using that serve the requests, and they have the most
some tooling to manage everything. But that won’t be “business” knowledge when it comes to how things
fully automatic. And there will be some OpenAPI-code should move underneath, let them write everything.
impedance mismatch. Especially since you will either Both the request schema, the request handlers, 

have both backend and frontend contracts generated and the clients for these requests. But instead of 

out of the OpenAPI schema, and both generators will using OpenAPI to design that, let’s use a language 

work slightly differently.

that is native to the backend and allows us to express


all the things that I was talking about earlier. 

And this mismatch is not the only thing that will cause The backend language is probably some general-
problems. The clients will work the way the tool wants purpose one, so parsing it & generating a client that 

them to work, which might not make sense to you. 

is based on it will probably be a not-so-hard task 

if we limit the feature set to only the vital things.
You will have to tweak them and then maintain them
manually. And if you use some exotic feature or design
something just slightly different than the tool authors
assumed, your code will break. Sometimes very subtly.
And this will push you towards manually writing 

a client for this single request, and then another, 

and then another, and you will have a set of manually
constructed requests that are unmaintainable,
probably broken, or altogether wrong.

Because of these, contract testing is a must - API


breakage will be too common to ignore. And finding
what broke will be very difficult without a robust set 

of tests. Or when the backend allows for some
ambiguity.

Flutter For Enterprise


This approach has a number of benefits: You have a strongly-typed client, so if something

changes in the types, you will need to account for that

when you update the contracts. And it won’t matter 



1 A single source of truth - the backend team
that it changed far away, in a request that you don’t use.

dictates how everything looks. Of course,

they do so after agreeing with the mobile


They don’t make the API versioning & supporting
team on how that needs to look. ;)
easier. You still need to do that, although particular

versioning requests are now slightly easier. You can


2 They know the flows, and they know 

also enforce that the API is backward compatible 

the data they need to manage, so they can
with proper linting.

preserve the meaning and communicate

that to clients using the same language.


And since you operate on high-level types and you
Plus, a sprinkle of in-code documentation.
have a limited number of types there (because, after all,

it is JSON), you can’t really express everything. 



3 Since they write normal code that is readily
And everything is typed, so there is less room for
usable, they don’t waste time doing
ambiguity, and sometimes you need to make things
documentation (that will, in the end, 

awkward.

be thrown away because the requirements

change).
Although there are problems, some of them small,

Since everything is code and code that is some of them amplified, all in all, it is a gigantic net plus
4

generated, there isn’t really a place for that simplifies the development and understanding of

technical omissions. Basic types are 
 the project substantially.

the same everywhere, so if contracts want 


an `int`, you will not be able to put 



Taming legacy
a `String` there. If everything compiles 


(both on the backend and client side), there


When we develop a large-scale project, we often
is a very high chance that it is correct.
make decisions that become outdated over time. 


5 /
Since the client is a normal Dart ( JS) code,
Also, those decisions will have to be made in the future,

so it’s inevitable. Our code is a legacy from the moment


the discoverability of it is exactly the same
we wrote it. If we wrote it again, there would probably
as the rest of the project - you just open 


“the API” in VSCode or your other favorite be other things that we’d consider. So it’s okay; 


we have to accept it. We can’t remove it, but we can


editor. You control how the client works, 

tame it.

so you can make it readable without

sacrificing functionality.
What can we do as developers?

6 The contracts are also easily versionable

when it comes to schema - you just use git First, we can deprecate things. If some function,

/
for that and point to a particular commit tag widget, class or even whole module is being used all

to use a particular version. Do not confuse around the code, but a new way of doing things has to

this with API versioning. You still need to do be introduced since business assumptions change,

that. then just deprecate it. In Dart (like other languages),

there is an annotation @deprecated that comes 


in handy for that. It’s really helpful because when there



What s best is that it s ’ Dart all the way, and every
are 20+ developers working on the project, they have
/
mobile developer is needs to be comfortable with it .

to know that this method or class shouldn’t be used 


and what is the correct way to do that.


Of course, it’s not a silver bullet.

This does not solve every problem possible 


and introduces a number of problems on their own.

Flutter For Enterprise


However, make sure you really deprecate. The worst Developers should always use design system
thing about deprecation is not obeying it. 
 components, and one squad should be responsible
This introduces even more corruption to our code 
 for the design system itself. In our organization, 

and communication since both ways of doing one thing it is the Overall Design Squad that is a kind of 

are still being used, and new developers don’t know “master squad” for all designers working on 

what to choose.

the project. It also has developers that, while


continuously interacting with UX/UI people, 

Also, we have to remember about broken windows 
 are developing design system concepts 

in our code. A broken window in the code means 
 in the codebase.
a code smell or something that obviously should be
done another way, but there probably was no time 

for that or for any other unpredictable reason. 

We have written more about our experience
That code will be emerging as more and more
in building and implementing design
corrupted because people wouldn’t bother to refactor
systems. On our blog, you can find more
things. That’s why we want to make sure about
articles about this topic and learn about 

deprecation. Because the objective is to encourage
the best practices.
refactoring, we want to minimize concepts that
discourage it - such as broken windows.

To ensure policies in our large organization, we have to You’ve been warned


maintain a technical squad because there must still be
an owner to ensure things. The technical squad owns
The areas you have listed are the tip of an iceberg.

everything that’s not related to any business domain -


CI/CD, tooling, cross-squad big picture things and one
Complex enterprise applications written in Flutter, 

of those responsibilities is taming legacy. That squad
as in any other framework, require special care from 

has to organize cycle status events where we can
a team of experienced developers. If you want to
monitor the progress of refactoring deprecated code.
enhance your own team with that experience, 

That’s why it’s very important to synchronize across
you should consider hiring our Flutter Developers, 

squads on a weekly basis or so. We don’t want 

who can form hybrid teams with your in-house
the inertia to grow silently as we come to the release. 

development team to work on building the next 

If we control it, then it’s not so much of an inertia after
state-of-the-art applications.
all.

Design system
Last but not least is that we should also apply similar
constraints when it comes to design. The design 

of our app basically means UI from the code side, 

and to make our user experience top-notch, we have to
be consistent. What does it mean? It means that good
app design should have consistent behaviors, 

a consistent color palette, consistent approaches to
similar user stories and so on, and so on.

Moreover, this should not come from the code


because the UX/UI and design don’t come from us -
developers. We merely implement the thoughts 

and concepts of the designers that we work with.
That’s why we have to maintain a design system, 

and we have to do it in tight collaboration with
designers.

Flutter For Enterprise


Author: Marcin Wojnarowski

Feature-Based Flutter Senior Flutter Developer 


at LeanCode
Architecture

At LeanCode, the architecture of mobile applications 
 This enables greater scalability and flexibility

is driven by the experience gained during 


the completion of many successful projects.

developers can work in parallel on different feature

code is less scattered, it is easy to find all code

This architecture scales from small to large projects. responsible for a featur

While some components of the architecture are enables feature-level innovation, the local

refreshed to adapt to new community standards, 
 architecture of a feature can be rewritten without

most remain the same due to being battle-tested 
 affecting other feature

on real-world projects. In this article, we highlight 
 self-contained and independent of other features

the design decisions made for the architecture when

developing a feature. This includes our approach 


to dependency injection, state management, 



Our comment section feature would look the

widget lifecycle, and data fetching.


following way:

social-app/

Imagining a feature lib

features

comment_section

As we said in the previous part, from a high-level


bloc

architecture perspective, we can see our application comment_section_cubit.dart

as a set of loosely-coupled features. We described comment_section.dart

how to organize them and do the wiring, so now we 
 widgets

can focus on how to build a single feature.

upvote_button.dart

Let’s say we are developing a feature for a comment

section under a post in a social media application. 


A user would be able to see the list of comments,


Feature entrypoint

upvote comments, and add their own comments.

Since this is Flutter, most often than not, an entrypoint


The file structure will be feature-based. This means

for your feature will be a widget. In our case, 



things related to the comment section will be closed

the widget is responsible for showing a comment


under the same directory. This is opposed to a type/

section. A feature entrypoint is required to set up all


function-based approach, where files are grouped by

dependencies used within a feature. This includes


their function.

external dependencies and those that will be injected

into the widget tree.

For dependency injection, we favor the community

standard package:provider. It allows for tying 


the lifetime of a dependency to a widget tree. 


The dependency is injected when the tree is created

and disposed once the tree is unmounted. The injected

values are also scoped to a specific widget tree, 


further assuring us of the self-containment of a feature.

Since provider is merely a wrapper around Flutter-

native’s InheritedWidget, we can leverage Flutter tools

without locking ourselves to a different paradigm.

Flutter For Enterprise


While we acknowledge the shortcomings 
 Managing state
of package:provider (such as not having the compile-
time safety when consuming dependencies), we State management for UI usually boils down to two
believe alternative solutions claiming to solve this concepts: data representing the state and some
problem such as package:riverpod introduce other, functions that alter this data. In this setting, UI is 

larger issues. a function of the state. package:bloc is no different 

and is our solution of choice. It introduces a clear
distinction between said data and functions. 

Let’s see the entrypoint of the comment section
This distinction promotes the immutability of state,
feature:
which then allows for a fully declarative UI. 

Additionally, the simplicity results in an easy to reason
about code.

is observed

to create
State UI

calls
change methods
which cause
This widget accepts in the constructor all data
needed to initialize a comment section. In this case, 
 Transforms
it is only the postId (1a) which is then passed to the
state manager (1b). Cubit for this feature is provided (2)
to the widget tree and will be automatically disposed
when unmounting. The cubit itself also needs the API
A Cubit is responsible for guiding the behavior of 

client to make requests, thus it is injected (3) (more 

the UI through its state. It does not have access 

on that in a later section). The ApiClient is considered 

to BuildContext, making it completely detached from 

a global dependency, as it is injected in the root of 

the rendering pipeline. This ensures greater separation
the application. Finally, once everything is set up, 

and testing in isolation. However, not all UI behavior
we return the child responsible for drawing all the UI
should be a function of the state. Most notably, 

and listening to the state manager (4).

one-off events that are not worth persisting in the state. 



In the case of a comment section, failing to upvote 

Other than a handful of globally-injected services, 

a comment could be an UI event. We don’t particularly
the dependencies of a feature are clearly defined by
care about remembering that it happened, but we
the constructor of the entrypoint.
surely want to make the UI reflect that it did. For this, 

we use package:bloc_presentation, which simply adds
Disclaimer: In really large apps, while the Flutter widget an additional stream to a Bloc for one-off events called
tree gets more and more nested, there could be
 presentation events.

an issue when StackOverflowError is being thrown


because of that depth. This issue is tracked on Flutter To learn more, visit the package's repository.
repository and can be found here, along with 

a workaround. This might occur when there are 

a lot of global Providers nested in each other on 

top of the widget tree. To avoid this, you can switch 

to another dependency injection tool. When it comes 

to global dependencies, we don’t need them bound 

to a specific element’s lifecycle. We have encountered
this problem only once in a project with >1M lines 

of code.

Flutter For Enterprise


Let’s see how a CommentSectionCubit would look And finally our presentation events:

like:

Making requests

At LeanCode, we design backends to be tailored for

the clients (“backend-for-frontend”). This means,

instead of having a generic REST endpoint /

posts/:postId/comments, which would probably return

extra data which the client wouldn’t use, or too little

data forcing the client to make additional requests 


to other endpoints, we design a dedicated endpoint 


for this mobile screen. 



This approach has the benefit of feature-level

endpoint optimization and, once again, isolation

between features. One less visible but still important

benefit is that it removes the need for the repository


The Cubit starts in an empty initial state indicating 


level. Instead, the API is already tailored to our needs,


that no work has yet been done. The initialize method

so we can directly make requests in a cubit through


fetches comments and indicates a hard failure in case

some API client.


of errors. In the failure state, no methods should work

since they will all most likely need the ready state to

access fetched comments. Recovering from hard For instance, the initialize method in the cubit would

failures is possible by calling initialize again or, 
 do the following:

better yet, through a dedicated refresh method 


(which would be called by a pull-to-refresh). 


In the upvoteComment method, we can emit 


a presentation event in case of an error, since failing 


to upvote is not a particularly interesting thing to

persist.

Where client is a generic HTTP client which can handle

For state, we use package:freezed which enables 
 request blueprints, and GetCommentSection is such 


an easy way to define union types with value-equality. a blueprint encoding all information needed to reach

This distinction between state types (initial, inProgress, the appropriate endpoint. At LeanCode, these

ready, etc) makes it clear which methods should 
 blueprints are automatically generated using 


and which shouldn’t be allowed during some state. our contracts generator, which gives us type-safe

backend-mobile communication.

CommentSectionState would look like the following:

Sometimes when we want to do additional processing

on the fetched data, a need for a repository arises. 


A notable example is caching/offline mode. 


The feature-based architecture allows us to make such

decisions on a feature-level. In such a case, we can

introduce a repository that encloses all additional data

logic and inject it into the cubit instead of the ApiClient.

Flutter For Enterprise


Reflecting state with UI Conclusions

Once we have a source of truth to draw the UI, a Cubit, In this part, we detailed how we build a single feature

we render widgets depending on Cubit’s state. 
 with its state management and proper separation

Due to the state being expressed as a union, we protect between UI and business logic. We also showed how

ourselves from rendering wrong widgets. For example, we can set up dependency injection and how to bind

we cannot show the list of comments while not being 
 lifecycles of logic objects and widgets to implement

in the ready state since we simply won’t have access 
 business-driven requirements.

to the list of comments.

With the knowledge from the previous part, we can

For our comment section, it is the following: scale this across multiple packages, teams and

organizations, so that we can damage control the ever-

changing project specification.

One last thing to handle is presentation events. 


We need to subscribe to the presentation stream 


and react to them appropriately. The act of

“subscribing” already implies that we need a stateful

approach. Classically this would be done with 


a StatefulWidget, but these widgets tend to be full of

boilerplate noise and be less declarative than 


one could want. As an alternative, we prefer to use

package:flutter_hooks. This package removes 


the boilerplate of a StatefulWidget and transfers it 


to a conceptual overhead.

Using a hook, we can setup a listener for presentation

events:

Flutter For Enterprise


The Role of UI Testing
Author: Julia Borkowska

Senior QA Tester & Head of QA

and How Patrol Can Help at LeanCode

You Improve Them

When it comes to testing Flutter applications, there 
 It’s not easy to be done though - UI tests are written 

are many approaches and tools that we can use to as code, and this code should work in a predictable
implement tests on various levels. way. This means that we need some tools to properly
distinguish elements on the screen and interact 

with them only when a real user would.

Starting from the bottom of the testing pyramid, we can


make use of unit tests - closely related to the app’s For example, such a test should only interact 

code. Another tool are widget tests, which we can bring with elements that are visible on the screen and also
into play as a kind of module testing, where bigger should know when to try interacting with them. 

elements of UI are tested in isolation. Focusing more 
 On the level of code, we usually don’t have
on layout, than business logic, we can utilize golden file straightforward information about what we can interact
testing - a Flutter-specific approach to screenshot with and when we should wait for something to appear
testing. on screen. And there are ways to do that - though 

we should keep in mind that these methods 

are complicated and are based on many technical
In this article, we’d like to focus on a solution nuances, which vary between different technology
incorporating testing functionalities and interacting stacks.
with real layout - UI tests, which unfurl their true
potential in testing large apps that are often only 

a part of much bigger systems. Regression testing - automated
The second highlight is that UI tests help in regression
UI tests - what do they really tests and they suit this task best. And there are
test? reasons for that. 

First of all, regression suites are precisely defined -


When planning, designing, and implementing tests, 

there is no need to alter the test cases spontaneously,
it is crucial to know what should and should not be
like in exploratory testing. It means that an automated
done on a specific test level. The main objective during
test can do the same task as a human tester would,
UI testing is to check whether the main purpose 

adding even more accuracy and repeatability 

of the app can be achieved by its users. Having this
in execution. 

general goal in mind, we can try to separate out, 



which particular goals our tests should target. 

Second advantage is that automated UI tests are
These targets specific for UI tests are a high-level 

significantly faster than humans, which means that
view of the system, big regression suites, and imitating
they cost much less than a big team of QAs.

real users' behaviors.


Finally - regression testing is the most tedious task 

To be a robot, but act like 
 in testers work. Automation makes it not only better 

in execution, but it lets the QA team utilize their time 

a human in a better way.
UI tests are the only kind of tests, in which we try 

to simulate real user actions - we build and launch 

the whole system, almost no data is mocked and we
want the test to perform user-like interaction with UI.

Flutter For Enterprise


Have a vista of your app Often they don’t have many opportunities 

to communicate with each other about how their 

Since we discuss large apps here, a big-picture view 
 parts interact, which is usually causing most of 

of what we created is very important. And no other the bugs detected by the QA team.

tests do it better than UI tests. Our scenarios are 



on such a level of detail, that they can correspond 
 To fill this lack of synchronization between many
with features, and test suites can comply with 
 teams, we need a point in the process where we
the app's modules. Not only can developers utilize would check our app as a whole product. In this case,
reports of such tests to monitor the health of 
 UI tests suit the best. Not only do we have a high-level
the system they’re creating, but business owners 
 view of features, but also we focus on interactions
can benefit from them too. between many parts of the system. It makes UI tests 

a perfect tool for verification while having many
Beware of overzealous independent teams working separately on the same
product.
Among aspects that are not covered by UI tests are
visuals, though they are called UI tests. We are Communication with business owners
interacting with the real UI of our app, but we won’t
check how it looks. Making sure the app is aligned 
 Looking at this process from another point of view,
with the designs is a task for the golden file tests. 

 business owners need regular updates on product’s
readiness and level of stability.

Our task is to make sure that this UI is functional. 

Having automated high-level tests, which take into


Another thing to keep in mind while writing UI tests 
 account all parts of the system, gives an opportunity 

is that they should cover only crucial user paths 
 to easily gather the results and present them 

and features. Many of the edgecases can be covered in the context of development progress and system
by tests on lower levels, such as unit and widget tests. reliability. Though other test results such as integration
It lets us not duplicate what we already tested 
 tests can provide similar information, UI tests 

and focus on high-level aspects and integrations 
 are already on the right significance level that 

that can’t be covered anywhere else. is needed from a business point of view.

Why are UI tests so important 
 Everybody hates regression


in enterprise-level applications?
Last but not least, regression testing costs grow
rapidly with the app’s size. In addition to cost, 

If we think about software development, we can point the time needed to perform every regression suite 

out some steps that are part of the software can extend to days of work of quite a big QA team.
development life cycle in every project, both small Finally, we all know how tedious work it is to execute 

startups and big-tech companies. Though UI tests 
 all of the scenarios, which makes it error-prone 

are not a popular topic among all developer teams, 
 and less efficient. Automated UI tests, once written, 

we see that this kind of test gains more attention 
 can be run in less time and without or with minimal
in bigger projects. And there are some reasons behind human supervision. The cost of maintaining 

it, which we will discuss in this section. and executing those tests is far lower than the cost of
manual regression. Additionally, saved time of the QA
team can be spent e.g., on exploratory testing 

More teams, different problems and other tasks which can’t be automated.

It’s clear to see that enterprise development teams 



are much bigger. Those teams are divided into smaller
ones and usually every smaller team is responsible 

for delivering one feature or use case of the app 

as described in a Flutter at Scale part of this ebook.

Flutter For Enterprise


Solutions Now, Patrol users can write their UI tests in Dart, using
simple yet powerful API, which lets them construct
Appium and its imperfections human-readable selectors and easily simulate real
user’s actions, both Flutter-native and OS-native. 

The most popular and universal tool for UI tests 
 We incorporate our own framework in many projects
of mobile apps is Appium. It is a black box solution 
 we develop for our clients, so we can perform better
that is designed for testing on many platforms. 
 tests and make our framework battle-proven i

Though when it comes to testing Flutter apps, 
 n real-life use cases.
this framework is not enough.

A well-known solution of that problem is using 
 Also, Patrol is open-source, so every developer can
a combination of Flutter Driver and Appium, which use it in their project, either commercial or not.

gives a possibility of testing both the Flutter side of 



the app and the platform native one - either Android 
 Recently, we released a new major version of our
or iOS. It enables us to cover more complicated use framework - Patrol 2.0. We reworked internals of how
cases, which incorporate not only those steps that 
 the tests are executed and integrated with the native
are performed in our app but also those outside 
 side, which enables our users to run Patrol tests 

of the Flutter code - e.g., email verification, logging in, 
 on device farms and improves their experience with CI
or registration by external services such as Google 
 pipelines. With new design, it is possible to utilize
or Facebook, reading SMS codes and many others. sharding on device farms, also time needed to run
many tests on the same app became shorter. 

We believe that those improvements unlock lots of
At LeanCode we were challenged to write tests 
 potential for new features and following refinements.
with Appium and Flutter’s integration_test package in
large scale apps to test complex functionalities made
See Patrol in action
with Flutter, while also incorporating some native
features. We quickly found that this solution required
Here we’d like to present some examples, 

an amount of work which was not acceptable 

how the tests look like when written with Patrol.

for our needs.


When you expect to see a permission dialog 

On the one hand, using the Flutter-native testing for sending notifications, you simply add this step:
package missed a possibility to fully interact 

with the native side of our app, such as providing
permissions for notifications or use of localization
services. We wanted to use a framework 

that is extensible, easy-to-use, and in which tests 

can be written in the same language as our app 

(in our case - in Dart). Knowing the limitations 

of Appium-Flutter-Driver specific design, we were
aware that we won’t be able to solve all of our current
Entering a text into a text field is simple as well - though
and future problems with this solution.
the first part of this instruction, which is responsible 

for searching for the desired element on screen, 

Patrol - our new framework 

can vary. Here we use searching by in-code type of
and the reasoning behind its creation widget, which is TextField in this case.
The experience with Appium-Flutter-Driver led us 

to write our own, Flutter-focused and open-source 

UI testing framework, named Patrol that was released
in September 2022.

Flutter For Enterprise


Summary

UI tests are a crucial part of test strategy 



in enterprise-level projects. Proper tools and good
practices of UI tests can significantly improve 

the quality of the developed product. From lowering
costs and time spent on regression testing, through
Scrolling and tapping on elements on screen is also
creating a point of synchronization for scattered teams
very easy. Here you can see a bit more complex
working on the same product to providing high-level
selector, still it is written in a descriptive way. 

visibility on the completeness of the system - UI tests
This step consists of scrolling to an arrow icon, 

prove their validity in many ways.
that is inside a list tile, which contains text “Part 1”, 

then taps on the icon that was found.

At LeanCode, we created Patrol having in mind big 



and complex, Flutter-focused apps developer teams,
who need a simple in use yet powerful and extensible
tool for creating the most challenging automated tests.

If you’d like to see Patrol in action or learn more about


how we made UI testing in Flutter a better experience,
visit our blog and Patrol’s website.

Please notice that we don’t have to write any code 



that would ensure the visibility of found elements - 

it is already taken into consideration by Patrol. 

Also, if there are many elements matching this selector,
action is performed on the first one by default. 

Of course, if you would like to search for the third one, 

it is still possible - by adding the “at(index)” method
after the selector, as in the example below.

All of those types as ElevatedButton and TextField 



are types of widgets that are either provided by Flutter
framework, or declared in app’s code. This access
makes our test greybox, which we consider as good
equilibrium for automated tests - the test doesn’t have
to know everything about the app, so it is easy 

to maintain it, but also we can differentiate elements 

on the screen in a convenient way.

Flutter For Enterprise


Flutter Add to App - Author: Marcin Chudy

Senior Flutter Developer 



Overview and at LeanCode

Challenges Based on
Real-Life Case

Flutter has taken the mobile market by storm. We often


hear from our clients that they are considering building
What is Flutter Add to App?
an application in Flutter. In the case of the enterprise
application, you often see the arguments that Flutter Add to App is a Flutter feature developed by 

can help you to either cut the costs or simply the Google team. You don’t always have to write 

streamline the development and deliver a much bigger a Flutter application from scratch. Flutter can be
scope in a given timeframe. However, it does not integrated into your existing application piecemeal as 

happen every day that you start building your mobile a library or module. If you have an existing native
application from scratch as a greenfield project. mobile application for Android and iOS, you can use
Flutter to render only some views in your application 

For those cases, it is relatively simple. With very rare or reuse some business logic in Dart between 

exceptions, you should use Flutter. Yet, what should the two platforms. Flutter Add to App can be added to
your approach be when you already have a native app iOS (Flutter can be incrementally added into your
working on a production, with its traffic, user base, 
 existing iOS applications seamlessly with Cocoapods
and a backlog of new features which are waiting 
 or with pre-generated embedded frameworks) 

to be shipped? There is hope, and it is called Add to and Android app (Flutter can be embedded into your
App. This is a hybrid application where fundaments 
 existing Android app piecemeal, as a source code
are in the natively written mobile application, 
 Gradle subproject or as AARs).
and certain components are built using Flutter.

When to use Flutter Add to App?


In this article, we will provide:
Cross-platform development is a very promising
Description of Flutter Add to App featur concept for Product Owners. It saves their time 

Pros and Cons of Flutter Add to Ap and money by maintaining one codebase between 

Challenges when using Flutter Add to Ap iOS and Android apps and makes managing 

Recommendations for using Flutter Add to App. the development team with a single Sprint goal easier.
Therefore cross-platform frameworks such as Flutter
It will be based on a real-life case from the banking or React Native became the first choice for 

industry, where for one of our clients, we have built 
 the greenfield projects like startups and new 

a corporate banking app. enterprise ventures. 

However, things can be more complicated if there 



is already an existing application (iOS or Android app)
and you consider changing the technology. In such 

a case, Flutter can be integrated, and this option 

is called Add to App. It enables the mobile development
team to add the Flutter features to the existing native
app. Yes, you heard it correctly, you can integrate
Flutter and take the benefits of cross-platform
development even if you have previously developed

a native code.

Flutter For Enterprise


Yet, there are strict cases for when this Previously the only chance to do so was to consider
adding the PWA component and displaying 

scenario is worth considering.
the integrated webview. However, you can stick to 


the components created in Dart with Flutter. This will


1 Rewriting the existing mobile application
allow you to deliver a better experience to the user 


on Android and iOS apps and save time on gathering


This is the most radical approach. It means that you

the two native teams for the same task on both


have decided to build the new app with Flutter, which

platforms.
will replace the existing native solution, but you don’t

want to suspend releases of the new tasks. 



5 Impro ving the UI of your current application
In that case, a Flutter Add to App will help you add 


new features to the existing Android or iOS app while One of the strongest selling points of building

replacing the existing ones with the new Dart code.



applications in Flutter is the ease of implementing

custom, complex user interfaces. And with Flutter 


Add the Flutter module scenario works best when 
 Add to App, it’s no different. This is where Flutter 


one team is working on new features and adding them can spread its wings and significantly speed up

to the app, and the other mobile team is working 
 development. Once you add Flutter to the existing

in the background on rewriting the native part. 
 application and starts drawing things on the screen, 


Such an approach is recommended for existing apps the UI development is the same flawless experience 

with a solid user base in a highly competitive market, as with pure Flutter apps. And the app's UI in Flutter

where it is vital to introduce new features. looks really well.

Getting the arguments for using


The con of mixing two technologies in a single app 

2
Flutter technology is that if you want the design consistent across all

(which is most often the case), many


screens
Suppose you are a Flutter advocate looking for a way 

components of the app's UI in Flutter need to be
to attract business stakeholders to the idea 

rewritten from scratch. It might be technically possible
of rebuilding your current mobile app in Flutter. 

to reuse some native components, but it kind of breaks
In that case, it is a good idea to showcase some small,
the purpose of using Flutter in the first place. 

additional features developed as Flutter add to app.
And for later maintenance, keeping all the UI
This will help you demonstrate the most significant
components in sync can be a significant managerial
advantage of this technology: ease and speed 

challenge.
of development. As a result, it increases the chances 


of getting approval for continuing to implement Flutter

throughout the app.


Using the Flutter Add to App
feature in our case

3 Building proof of concept type of apps

At LeanCode, we had an opportunity to develop 



an add-to-app Flutter proof-of-concept for a big 

The main point of building proof of concept is to test

client from the banking sector. Our goal was to prove 



whether or not a particular concept is possible 


and beneficial from a technical point of view. This is


that Flutter was the right technology for a new mobile

similar to the point mentioned above. However, 



application that was to be written from scratch .

it means that you don’t need to release that feature 


to your end users but only showcase it to some internal


As part of the PoC, we verified some native features 

stakeholders. This is enough to prove whether your
like camera, biometry, and animations. The main part

mobile app's performance will be satisfying or not.


was to rewrite one complex business process 


in an existing native app. The process involved

4 Implementing a small, isolated feature integrating with existing native screens and A PIs,
feature flags mechanisms, and doing some
Let’s assume that your current native mobile app 

background processing. Considering the sector,
fulfilling strict security requirements was also highly
is working and you don’t have the native team

assembled to perform new tasks. Yet, you want to add


important.

some fairly isolated features.

Flutter For Enterprise


Challenges of introducing First, we discovered that app bars in the native iOS
app used a custom animation for screen transitions. 

Flutter Add to App feature It quickly turned out that it didn’t play well with Flutter.
While it seemed technically possible to handle such
First of all, the official documentation leaves something transitions gracefully, it would involve much work 

to be desired. Flutter Add to App from the start is not to make the animations go well together. Due to all
the default, recommended way of creating apps with those issues, it turned out that it might be faster 

Flutter. The general process is described in the docs, to rewrite more screens in Flutter (especially as the UI
and it seems straightforward. When it comes to was not that complex) than to look for a workaround 

integrating with a big, long-lived native app, where to keep those screens native. And in fact, we did just
many things often had been implemented in a custom, that - a few screens that weren’t initially meant to be
non-standard way, the integration can cause many rewritten in Flutter were migrated to Flutter. That way,
hard-to-solve problems. 

we could keep the whole process working in Flutter


and avoid extra-native communication. The entire
It's nearly impossible to predict all edge cases, 
 business logic stayed multiplatform in Dart.

which can cause disruptions in the entire application


development flow. Having multiple engines (instances) We also discovered that the native iOS app had some
of Flutter in more advanced integration scenarios generic view controllers that didn’t play well 

increases the complexity. The risks are reduced when with Flutter screens. We had to develop a custom
we use the more straightforward, isolated approach 
 wrapper for that need which was not a lot of extra work,
of adding Flutter or integrating with smaller native but it required some native knowledge, and it could be
apps. Still, some help and engagement of native tricky for many Flutter devs.

developers must be considered while planning 



the work.

The business process we were to develop was


intrinsically asynchronous - it involved checking some
You also need to keep in mind that Add to App 
 data in the background and displaying dialogs when
as a long-term solution can decrease the efficiency 
 the flow changed. It meant that we had to develop 

of your app. In most cases, you would need to maintain a way to show Flutter dialogs at any place in the app,
two UI component sets and a bridging layer with both over native and other Flutter screens. It involved
developer experience decreased due to context maintaining a couple of Flutter engines and managing
switching and longer build times.

their lifetime, a challenge that is strictly connected 



with the Add to App integration.

When you have decided on Add to App, 



we recommend isolating the Flutter module as much If your app necessitates transitions between Flutter
as possible in place of a complex integration process and native screens, every such path has to be taken
because it needs a lot of extra work and brings issues care of. For every such transition, a bridge has to 

that don't exist in pure Flutter apps. Some complex be created (in Dart, Kotlin, and Swift). You can make 

technical topics can be researched on the side 
 the bridges pretty generic, but that still adds some
without touching the native app to avoid time spent 
 development overhead that wouldn’t be the case 

on integrating with native parts. Remember that 
 for a pure Flutter app.
the development effort can vary concerning code
quality and the technical debt of native applications.

Navigation in native and Flutter


projects
The navigation is where the native and Flutter
definitely need to cross. Flutter Add to App, 

by definition, means native and Flutter screens work
seamlessly together. In our case, though, things
appeared more complex.

Flutter For Enterprise


Networking between the native With a pure Flutter app, due to the myriad of libraries
available and the Flutter approach to rendering, 

and Flutter app one rarely needs to develop and compile native
Android and iOS code (which can take quite a lot of
Almost every app communicates with backend time).

services of some sort. Unless the Flutter module 



is heavily isolated (e.g., depends on some third-party Hot reload and hot refresh is very fast, 

APIs that are not used by the native app), another and the developer can immediately see all changes 

communication point arises between the native app in the code. With native recompilation, all screen state
and Flutter. is lost, and a developer needs to click through all
screens to retest a feature. Developers lose time
recompiling native parts and often need to debug
across many IDEs, which hurts productivity. 

When rewriting an existing native module in Flutter,


two approaches can be taken:
Another interesting thing is that due to the lifecycle 

of Flutter engines in Add to App, the screen state gets
The first is implementing networking (headers,
cached. So if you’re, for example, going from a native
serialization, etc.) in Flutter with the thinnest
screen to a Flutter screen, do something on it, then go
possible bridge to handle authentication. 

back to the native screen, and then again to the Flutter
Usually, the authentication flow will have already
screen, it will look exactly the same once you have left
been implemented natively before adding a new
it. This is often an undesired behavior. The difference 

framework to the app. Some edge cases 

in the screen lifecycle can be pretty confusing 

with refreshing tokens and keeping requests 

to developers. They would expect the screen code 

in the queue to preserve order can be tricky 

to run from square one, but it doesn’t. To properly
to handle.
handle that case without destroying the engine, special 

The other is to reuse the existing native networking, native bridges are necessary.
keep all endpoint definitions over there and expose
a full-on client to Flutter. The latter can make sense
if the protocol is complex and heavily customized, Native libraries in Flutter Add to
so reimplementing it in Dart for a PoC is
App
unnecessarily expensive.

While the UI is pure reusable Flutter, almost every app


needs some sort of native integrations (camera, push
Issues with debugging in case notifications, storage, geolocation, etc.). Those need
Flutter libraries containing native code. Adding such
of multiple engines plugins is a very straightforward process in a pure
Flutter app (usually, adding it is limited to changes 

One of the reasons Flutter became so popular 
 only in the Dart code). 

and is considered a mobile framework of choice 



for many developers worldwide is the developer With Add to App, some native libraries may require
experience it provides.

extra configuration. It is often undocumented as those


libraries are not designed and tested with Add to App 

Once you’re only using one Flutter engine in your app in mind.

(i.e., the Flutter part is heavily isolated) debugging


experience is the same as with pure Flutter apps. 
 The native app we worked on did not use Cocoapods
But for multiple engines, we have found issues 
 on iOS (a package manager for which Flutter Add to
with debugging multiple Flutter contexts. 
 App has built-in integrations). Because of that, 

The debugger wouldn’t attach to some engines, and we were forced to embed all native frameworks 

the hot reload would not always work. in the native app manually. That meant building
frameworks, opening XCode, and manually adding
them for each native library, an action that is easy to
forget and tedious for developers.

Flutter For Enterprise


On Android, on the other hand, we were forced to use
Flutter fragments instead of activities because 

App size when using Flutter
of the existing native app architecture (activities 
 Add to App
and fragments are two different types of UI
components on Android). As Flutter has its rendering engine and runtime, 

it will impact the app size. The increase is not
While it is not a problem in itself (Flutter supports dramatic, but if the app size is very important for 

fragments), we later realized that some native libraries your app, it needs to be kept in mind. Remember 

we had installed did not work as expected. 
 to measure size only on release builds. Debug build
It turned out that some extra configuration 
 sizes are definitely not representative, as they contain 

was needed, and the documentation was written 
 a lot of tooling that only developers need.
only with activities in mind.

The Flutter module we developed consisted of a few


Background services 
 Flutter screens, some assets, and a limited number 

and Flutter Add to App of pub libraries. The size increase for our app was 27
MB for iOS and 48 MB for Android. However, the
Implementing some background services in the mobile Android app was still being deployed to Play Store 

app was necessary as part of the process. We needed in a deprecated APK format. Uploading in that format 

to check for status changes periodically, and the logic is no longer possible for new apps. It is now required to
had to continue executing even when the entire use the superior AAB (Android App Bundle) format.

application was closed. We could simply execute 



the existing native background services, but our goal The problem with APKs is that they need to contain
was different - the business logic had to be a platform- binaries for multiple architectures. In the case of AABs,
independent module in Dart. We wanted to prove 
 the app that the end-user downloads from the Play
a hypothesis that we can achieve high code reusability Store is optimized for their device. With AABs, 

even in more complex cases.

the effective increase would be 3-4 times smaller.


Changing the deployment format and, thus, 

Obviously, for all of that to work, we still had to optimizing the size of the app with Flutter can be 

communicate with native code. The background an extra organizational effort.

process had to be a separate Dart isolate (a concept


similar to a thread), which meant it needed to be run in If your app contains assets that are also to be used by
a separate Flutter engine. The engines can Flutter, they can be shared between native code on
communicate between themselves (in pure Dart), 
 iOS, but unfortunately, at the time of writing, this feature
but they consume more resources. The Flutter team is not yet available on Android. It means those assets
introduced a new concept called engine groups which will need to be included twice in the final app bundle,
brings huge optimizations, but it was still unstable 
 and they will increase the overall app size.
at the time we were using it. The infrastructural logic
and turning on and off different engines must remain
native.
Flutter engines and
 

We implemented a bridge for talking with iOS 



performance overhead
and Android using Pigeon - a handy but not that well-
When you’re adding Flutter runtime over a running
known tool yet for generating typesafe contracts 

native app, it is impossible not to add some extra
for Dart, Android, and iOS bridges. It needs to be said
performance overhead to the existing iOS and Android
that code for managing engine lifecycles can be tricky,
app. Flutter needs its own execution environment 

especially for developers not experienced with native
and even multiple environments in more complex
development. For example, a lack of a more profound
scenarios.
understanding of the reference counting mechanism 

in Swift can cause unexpected crashes.

If you want to find out how to implement background


services in the Flutter Add to App, go to this article.

Flutter For Enterprise


The most expensive operation is starting 

(pre-warming) the Flutter engine. All assets 
 There are two basic approaches. Firstly, you can
and the Flutter library must be loaded, a Dart virtual install the Flutter pipeline on build machines - it’s the
machine has to be started, and the entry point code most straightforward way. Adding a few extra steps
has to be executed. Depending on the use case, 
 (Flutter build, tests, etc.) will complete the Flutter Add to
this can be done at app startup, after the app displays App integration.

some initial data to the user, or only when the user


opens the Flutter screen. It all depends on when 
 If that’s a problem in your organization, e.g., due to
you’re willing to sacrifice the extra time that the user security concerns (as it happened in our case), there 

has to wait. While this can mostly happen 
 is a way around in which you can build the Flutter part
in the background, Flutter still needs to block 
 to native AARs (for Android) and xcframeworks 

the main thread for up to a few hundred milliseconds (for iOS). Then, theoretically, it’s possible to commit
during initialization. those built artifacts to the repository and keep 

the existing CI process untouched.
With the engine being prewarmed, rendering the first
Flutter UI frame also has some latency. In our testing, 

it was around 100ms slower than rendering the native It will, of course, lack testing and static analysis 

screen. This was completely acceptable for our case of the Dart code, but it will suffice for publishing the app
and hard to be noticed by the user. 
 with Flutter for some quick testing. There are still
Times of subsequent renders were comparable 
 problems with Android, which requires network access
to native views.

to some Flutter Maven repositories. We quickly found


out that those were blocked in the corporate network.
When you open a Flutter screen and then go back 

to a native screen, by default, the Flutter engine keeps
running. The UI state is cached so that subsequent Add Flutter to an existing app
screen openings are faster. This keeps eating 

the CPU, and you need to keep an eye on RAM.

vs. organizational problems


When your Flutter process is not isolated, there’s 
 Introducing Flutter in a large organization 

a high chance you will need multiple Flutter engines. and introducing it as part of an existing native
Every Flutter engine needs resources to run, 
 application brings a lot of challenges 

so the performance overhead increases. However, 
 on the management level. Large corporations often
the Flutter team has recently introduced a new concept use proxies, artifact management systems for libraries,
called FlutterEngineGroup. It makes it possible to share and custom certificates. The network traffic can be
resources between multiple engines so that 
 heavily restricted.

the overhead of running another engine is minimal.


This means significant improvements for more Flutter development may therefore require close
complex Add to App scenarios. cooperation with other teams to resolve those issues
so that developers can set up their development
environment on their machines. This process can take
Continuous integration 
 some time, and one has to remember that it can
in Flutter and native apps completely block development and waste resources.

For native apps, you most likely have some Continuous When the Add to App idea is to rewrite an existing part
Integration system set up so that new iOS or Android of the iOS and Android app in Flutter, you have to
app versions are automatically published for testers. consider whether the whole business process 

The build process can also be integrated with Google has up-to-date documentation. For long-running apps, 

Play Store and App Store so that deployments take the documentation is often outdated, and in the end,
minimum effort. While Flutter builds to native platform developers will need to analyze the native code to
code (APKs and IPAs), some changes to the CI process understand how the app works. This is much slower
are required. than working with well-described requirements,
especially if the code quality is poor.

Flutter For Enterprise


Introducing Flutter can also bring some tension 

with the native developers currently working 

Conclusions on Flutter Add to
on the application. They will need to install and set up App
Flutter locally to continue working locally. Suppose that
brings problems with the environment, workstations, 

or security. In that case, Flutter devs can deliver pre- T he success of using this approach depends hugely
built native artifacts (AARs and xcframeworks) 
 on the choice of the feature you want to implement
so that native devs don’t need any additional tooling 
 with Flutter. As you saw from the list of things you need
for the Flutter project. The approach is suboptimal 
 to take into account, whether they will hurt is subject 

due to the extra manual work required from the Flutter to the proper isolation of that feature from the native
team. environment. ake this choice wisely, and you will be
M

able to avoid most of the hurdles we have described.

How to add Flutter to an


existing app - approaches Remember as well that Flutter Add to App is not 

the default approach for Flutter app development. 

So be clear about your vision and what you want to
Understandably, if you would like to rewrite your
achieve with this roof-of- oncept Flutter-based
P C
entire application in a new trendy cross-platform
feature.

framework such as Flutter right away, it could be


perceived as too huge of a revolution. With Flutter,
Whatever the choice you make regarding the feature
though, you can start step by step.

set, it is important that you will be assisted by a team 


of skilled and experienced Flutter developers.



Flutter can be added to existing native apps, even only
as a single screen or even as part of the screen. 

Since Add to App is purely documented as speci c fi
Adding Flutter in such a way can be a small proof of
skills depend on the existing native project, 

concept to manifest the new technology in your
all architectural choices, as well as the execution, 

organization. You can also implement the same feature
are demanding, and high expertise is required. 

natively and in Flutter to compare performance 

Explore this ebook further on how we can assist you 

and user engagement with A/B tests.
by providing the Staff Augmentation . services.
2 0

We can take several approaches when considering


how to include Flutter in a native app:
Self-contained Flutter module - when we have a
business module that is ideally kept in one place in
the app (e.g., one tab of the application). We can
implement it in Flutter 

and restrict native integrations to a minimum
Hybrid navigation - when the business feature we
want to implement in Flutter crosses paths with
some existing native screens (e.g., we can go from a
Flutter screen to a native screen 

and then to another Flutter screen). This approach
brings some additional technical challenges
Mixing native and Flutter views - it is technically
possible to render some parts 

of native screens with Flutter (and vice versa). This
is the most complex and often suboptimal way of
using the Flutter Add to App.

Flutter For Enterprise


Building Large Scale Author: Łukasz Kosman

CEO & Co-Founder at

Apps with LeanCode - LeanCode

Staff Augmentation 2.0

When you are a CTO or a Tech Lead for a new venture Let’s say that you hire 4 Flutter Developers 

in an enterprise, you are faced with a lot of from LeanCode. What you get in return is 

challenges. On the one hand, you have an option to the guarantee that whatever problem or challenge 

finally take responsibility for the system architecture you want to address, there will be 60 top IT Specialists
and its delivery. This is for sure satisfying. On the willing to help.

other hand, there are plenty of decisions to be made


which can impact future success. N eed a further example?

Are you building for the big scale from day one? Will the While developing the product, our team stumbled upon
business deliver enough traction to justify a complex a specific requirement, for example, payment card
architecture? How to balance the quick time-to-market provisioning from Mastercard and Visa. In that case, 

of new features and the requirements for the complex a domain expert from our Flutter Team is called to
architecture? Those are all tough decisions, 
 provide guidance for the feature architecture 

and sometimes you need a good sparring partner 
 and to share best practices. This collaboration goes
who is experienced in similar projects. beyond the specific field of expertise. We often have
cases in which the Flutter teams are using the support
At LeanCode, as a tech partner for our clients, 
 of our Backend Architects to facilitate the modeling 

we are not another team augmentation provider,
of contracts with the in-house server-side team.
but we actively help you to make good strategic Whatever the challenge, the chances are that in one 

decisions and help you reach your business objectives. of our 60 projects, we have already encountered 

We also help you to scale quickly, but adding 
 a similar case.
our qualified, English-speaking team which makes 

a real difference. Last but not least, we can support you D evelopers from the best Universities only
in your own recruitment and training efforts so that, 
 (no career changers)
in the end, you build your own team while delivering 

a great product. Our entire team graduated from computer science
studies at the best technical universities in Poland,
Interested in learning more? Let’s go! where the vast majority comes from Warsaw
University of Technology. Moreover, we are also

leading two courses at that University about


Why Staff Augmentation 2.0? Developing Mobile Apps in Flutter and System

Architecture in .NET.
While engaging with different clients, we often saw 

that typical outsourcing companies only recruit 

Knowledge transfer supervised by our
and sell profiles. At LeanCode, we promote

the knowledge transfer and personal growth of all Team Leaders
developers and experts. This is why we can provide
We hire professionals who can indeed lead 

this knowledge and expertise to our clients.

the development teams. They are the experts in their
respective fields. They can supervise the team 

How does it work in practice?
of in-house developers to facilitate the knowledge
transfer to their development team and ensure 

that everybody is on the same page.

Flutter For Enterprise


Professionals with excellent Training for the local IT workforce
communication skills
Is your mobile development team not experienced 

It’s not only about the language we use, but all 
 in Flutter? They can form a hybrid team with 

our developers can speak fluent English. Moreover, 
 our Engineering Managers, who will teach them 

our staff augmentation team is a perfect fit for any the best practices. We practice teaching skills at
company which embraces open communication 
 Warsaw University of Technology, where our experts
and transparent culture. lead two courses.

Conference speakers and community Help in your future recruitment processes


members
What will happen once the project is over? 

Within our team, you can easily spot popular We have no vendor locks. Even our own libraries 

conference speakers who are widely respected by 
 are open-sourced, and you can easily maintain them. 

the international tech audience. They are true experts We can offer the SLA offer for the 2nd level of support 

and can inspire an in-house team to cooperate better of the projects, but we also have experience creating
together. job postings and recruiting new software developers
for the in-house teams to help you with the in-house
team expansion.
Engineering managers experienced 

in enterprise projects
Building a startup in-house and creating a truly
scalable product ready for demanding security 

and performance tests often conducted by third parties
are two different animals. Our developers are
experienced in large-scale software development
projects with different-sized augmented teams starting
from 6 to 200 team members and can bring those 

best practices with them.

Bulletproof expertise
As all experts, we are opinionated. It makes life 

so much easier to follow specific architecture choices,
especially when they have been through rigorous
penetration, security, and performance tests 

and are based on eight years of experience.

Moreover, our standards are updated to catch up 



with the technological edge and are regularly reviewed
to meet the highest standards. As a staff augmentation
partner, we bring this experience to keep your mind 

at rest.

Sparring partners
In a leadership position on a tech side of a project, it
gets lonely quickly, and the responsibility for the
decisions is vast. CTOs from our clients like to
exchange ideas with our Team Leaders and
Engineering Managers and work together hand-at-
hand to make the right architectural decisions.

Flutter For Enterprise


Not just another staff augmentation provider
At LeanCode, we love complex cases. In our core technologies, we have been working on almost all possible
technical challenges, and we love discovering something new. As your outsourcing vendor, we will happily tackle 

the most troubling problems, build proof-of-concept projects and transfer the knowledge back to your team.

The reviews we have, speak for themselves

“LeanCode has delivered the MVP within 2.5 “Solid knowledge of LeanCode and trusting
months, exceeding our expectations. Agile cooperation help us deliver new features 

and detail-oriented, they've taken the time 
 to our customers quickly, continuously, 

to understand the banking industry to deliver and based on high-security standards. 

the most effective solution for our users. 
 The sophisticated code base and experience
They are professional, efficient, 
 within LeanCode provided us with a strong
and responsive.”
foundation for the IT security certification
process.”

Tomasz Czerwiński Mario Martella


Deputy CIO at Credit Agricole Bank Polska

Poland is the best outsourcing destination you can imagine


During his recent visit to Poland in May 2023, 
 For us living and breathing in Poland, those statements
Sam Altman, the CEO of OpenAI, mentioned that 10 out are hardly a surprise. Rooting from the communist
of 50 initial OpenAI Developers in the Core Team were times, we have an excellent foundation for STEM
Polish. He wondered, "I don't know what Poland 
 courses in our universities which quickly transformed
does to create such an amazing engineering talent". 
 from the coal and heavy industry into computer
This same observation was shared recently by Visa, science laboratories. With a very good command 

which decided to build a tech hub in Poland for 
 of English and the mindset of hardworking specialists,
a 1.5K IT workforce. Polish IT Developers significantly impact the tech world
in general.

Flutter For Enterprise


Additional opportunities from .NET
our staff augmentation service This is the core backend technology at LeanCode. 

We have our own framework, which is built on top of
Short mobilization time .NET and streamlines the work. It is battle-tested 

in 50+ projects.
With a team of 60+ IT experts, we can quickly scale up
the dedicated team according to the project's needs. React.js
Typically our projects can start within up to four weeks.
For one of our clients, we have assembled a team of 20 We remain attached to the proven web development
experienced Flutter developers within three months. technologies for regular, customer-facing web portals.
This would not be possible using the traditional Check our case studies to learn more about the React
recruitment process. portals we have built.

Flexibility and part-time team members Node.js


You can choose what availability is the best match 
 Great fit for teams who want to embrace the full-stack
for your project. Imagine, at a particular stage, 
 development experience.
you need only 1/2 FTE of Quality assurance specialists
for automated tests. Our flexible team augmentation
services can help you to get a custom offer.
DevOps
Our backend developers are typically taking care 

Special consulting sessions on demand of the infrastructure in their projects. Our primary field
of expertise is Microsoft Azure, but we have developers
One of the benefits of working with an augmented seasoned in other cloud solutions like Google Cloud 

team from LeanCode is that you can tap into 
 or AWS.
the knowledge and experience of all our teams. While
we were working on an enterprise Flutter mobile app, UX Design
one of our clients needed to consult the complex
backend architecture. Although we were not Our dedicated team of UX and UI Designers can build
responsible for that part, one of our Team Leaders from an excellent Design System and bring your vision 

the backend guild took an active role in shaping 
 to the tangible, high-fidelity prototype in Figma.
this architecture and worked as a sparing partner 

for the Technical Team Leader on the Client's side.

In terms of technology, we are


very focused
We are a boutique software development studio 

that is highly specialized. We do not provide experts 

in all possible technologies, but when we focus on a
specific technology, you can be sure we have
mastered that.

Flutter
We co-founded Flutter Europe and Flutter Warsaw, 

one of the biggest communities of Flutter Developers 

in Europe; we are contributors to Flutter and build 

our own open-source libraries. Our team of 30 Flutter
Developers is one of the most experienced teams 

in mobile development using Flutter worldwide.

Flutter For Enterprise


What is a typical staff The second model is called horizontal. 

augmentation model? In this model, the augmented team cares about 



a particular app layer. For example, at LeanCode, 

There are several scenarios in which you you can hire a team of .NET Developers who can be
responsible for the backed side of the project and feed
can adopt a staff augmentation model.
the contracts to the frontend developers working 

in an existing in-house team.
The first one is called vertical.

It means that the additional team is supplementing 



Horizontal Dedicated Team
the efforts of the current in-house team by adding
another development squad. This staff augmentation
model has roots in a feature-based team organization
where the in-house team is leading squads responsible Product Owner

for the different features, and the dedicated team takes


care of just a particular set of business requirements.
For example, let's say you want to add in-app credit Frontend/Mobile Devs Frontend/Mobile Devs Frontend/Mobile Devs

card provisioning to the existing mobile app.

In that case, a separate vertical team can take care of Backend Developers Backend Developers Backend Developers

the backend integration and mobile development of


this feature, isolated from the other squads. In this
model, the team has a separate project manager and The last model we call a hybrid software development
product owner supervising this augmented team. 
 model.

In this model, the local team capabilities are multiplied


by adding new team members with comparable In this hybrid mode, developers of all skills are mixed
skillset. to form a coherent team. In this case, we provide
experienced Flutter or .NET Developers who work
hand-at-hand with the local team. This model is based
Vertical Dedicated Team on a tight collaboration where external and internal
developers run each other code review sessions 

Dedicated team Existing teams
to enhance the knowledge transfer. The main
advantage of this cooperation model is that the local
Feature Squad #1 Feature Squad #2 Feature Squad #3
team acquires the know-how.

Hybrid Dedicated Team


Product Owner Product Owner Product Owner

Frontend/Mobile Devs Frontend/Mobile Devs Frontend/Mobile Devs


Product Owner

Backend Developers Backend Developers Backend Developers

Frontend/Mobile Devs Frontend/Mobile Devs Frontend/Mobile Devs

Backend Developers Backend Developers Backend Developers

Choosing a suitable staff augmentation model hugely


depends on your internal resources and what you can
provide internally.

Flutter For Enterprise


How much can staff How to start with our staff
augmentation services cost? augmentation?

Well, as always, it depends. This process is not linear,


In short, this is very affordable in the short run. 

but the stages below can give you an idea of what you
It helps you to scale your product development efforts
can expect from us.
in no time. It gives the confidence that architectural
decisions are made right and that you can rely on your
freshly hired and already integrated IT team from day 1 Schedule a call. We need to understand

one. Neither the in-house recruitment process nor your your situation better. At this stage, anything

own training will bring you comparable flexibility 
 can happen. If you have already built some

and high performance. 

 MVP, we are happy to run a technical audit


on that. If you have stumbled on some hard

In the mid-to-long term, the profitability 
 things in the system architecture, we are

and affordability depend on several items. 
 pleased to refactor them. If you have

If you choose your outsourcing partner wisely, their identified specific risks, we can build 


costs could be very advantageous. Eastern Europe has a PoC to mitigate certain risks if you have

an excellent IT talent pool that you can tap into for identified them. As said before, it depends.

valuable resources. At LeanCode, we bring world-class


2 Review the team. Based on your
Experts for highly competitive prices.

expectations, we will preliminary select


candidates who fit your needs well. You can
While securing the budget for staff augmentation, 

review their profiles, and we will arrange 

you need to consider the size of the requested team,
a call to check if it clicks between you 

the skillset required, and the length of the contract. 

and the team.
You can negotiate better prices if your vision is long-
term, as it brings stability to the project and the team.
3 Sign the Agreement and the first Order. 

Our Master Service Agreement handles 

the transition of IP and several other
essential elements. However, it doesn't
oblige you to pay anything. The actual works
start after the Order is signed for a particular
team for a specific period.

4 Build a Partnership with us. We like to meet


regularly, at least once a week, to monitor
the project's progress on regular statutes.
Once a week, we also ask you in a survey
about your satisfaction and collect your
feedback. This helps us hugely to be a better
partner and to provide the best staff
augmentation services.

Schedule a call

Łukasz Kosman

CEO & Co-founder LeanCode

Flutter For Enterprise


About LeanCode

LeanCode is a Software House from Warsaw, Poland, and a leading provider of


the native mobile applications built with the Flutter Framework.

We have a team of 60+ developers, The majority of our clients represent


designers, product owners, scrum the Banking and Fintech industry, but
masters, and QA engineers who we also develop products for
support the development of mobile and Marketplaces, Logistics companies,
web applications using Flutter, .NET, SportTech, MedTech startups, and
React, and other technologies. others.

We work with clients from all over the


world, including the USA, UK, Germany,
and Australia.

Our services include:

Mobile App Mobile Apps


Web
Staff
 Product
IT Consulting
Development Audit development Augmentation 2.0 Design

You can find out more about our core If you have any project in mind, we are
technologies, services, and delivered always open to discuss your needs and
applications on our website:

possibilities. The best is to reach us via:

leancode.co

Get an estimate form

See what’s new at LeanCode on:

You might also like