Sprint 4 - Group22-MobileApp/Grupo22-Moviles GitHub Wiki
The mobile application provides university students with a centralized, convenient, and cost-effective platform for buying, selling, and trading academic materials within their campus community. By facilitating the exchange of second-hand textbooks, lab manuals, and other supplies, the app significantly reduces costs for students, making higher education more affordable. Additionally, it promotes sustainability by encouraging the reuse and recycling of materials, thereby reducing waste and environmental impact. Features such as seller ratings and reviews enhance trust and ensure a safe and reliable marketplace, while the platform's user-friendly design streamlines the process, saving students valuable time and effort.
Operating as a non-profit organization, the app's revenue model primarily relies on donations, grants, and sponsorships. Donations from users, alumni, and philanthropic organizations are facilitated through easy-to-access links within the app, ensuring continuous funding without compromising the core free functionality for all users. By maintaining transparency and respecting user privacy, data is anonymized and may be used for research purposes in collaboration with academic institutions, enhancing the app's credibility and contributing to educational advancements. This funding approach ensures that the app remains accessible to all students, aligning with its mission to reduce financial barriers and promote sustainability in education.
-
The initial approach to micro-optimization involves eliminating any unused imports or files within the application. The objective is to avoid loading unnecessary libraries or packages that won't be utilized later on the view. The reduction in consumption is modest, decreasing from just above 128MB to slightly less. Before
After
-
We use lateinit to defer the initialization of variables until they are actually needed. This helps us manage resource-intensive processes, such as checking for internet connectivity, more efficiently. By delaying initialization, we avoid unnecessary memory usage and computational overhead during the early lifecycle of the activity.
-
We use SVG for every icon, logo or simple image. Due to the SVG files takes up less space in memory than the normal jpg or png files.
1). We used Future _showItemDialog method in three different views of the app: itemGallery(), allItems(), and likedItems(). This method is used to show a dialog box with the details of the item selected by the user including the item name, item description, item price and owner information among others. The method is called when the user taps on an item. Since the method is used in three different views, we decided to implement it in a new class called DialogService. This way, we avoid code duplication and improve code maintainability, a good practice for micro-optimization.
2). When we execute the dart - -fix apply command, we usually generate a lot of const keywords in the code. This is because the dart - -fix apply command automatically adds the const keyword to avoid unnecessary widget rebuilds and improve app performance. Also the const keyword is used to create immutable objects which are more efficient in terms of memory usage and performance. Finally, the const keyword is used for stateless widgets constructors. This is a good practice for micro-optimization.
3). We used the key property in the ListView.builder widget in the home(), itemGallery() and searchPage() views. Instead of using only ListView
, we added the .builder to efficiently render large lists improving scrolling performance in list views. This keyword helps building the widgets that are currently visible on the screen and avoid unnecessary widget builds. This is a good practice for micro-optimization.
4). We use image network caching to improve the performance of the app. Also shared preferences are used to store the user's liked items and avoid unnecessary network requests. This is a good practice for micro-optimization.
5). ItemGallery() used to have a really nested widget tree, but we refactored it to have a flatter widget tree. Flutter has a tool called Widget Inspector that can help you visualize the widget tree and identify areas where you can reduce nesting. Also by the commands ctrl + . and ctrl + , you can ask Flutter to refactor your code and reduce the nesting of widgets by moving them to separate methods. This is a good practice for micro-optimization.
Id connectivity scenario | 1 |
---|---|
Event description | The user tries to log in to the app but when he sends the petition there is no response because he lost connection |
System response | The user should be informed that the login was not successful due to his lack of connectivity |
Possible Antipatterns | 3 |
Caching + Retrieving strategy | 1 from our list |
Storage Type | No clue |
Stored data type | Key value <NPI, NPI> |
Behavior in our app | ![]() |
Id connectivity scenario | 2 |
---|---|
Event description | The user opens the app (he was already logged in) and is on the main page when he suddenly loses connection |
System response | The user should be informed that he doesn't have a connection but he should still be able to see the content that was cached before he lost the connection |
Possible Antipatterns | 3 |
Caching + Retrieving strategy | 1 from our list |
Storage Type | No clue |
Stored data type | Key value <NPI, NPI> |
Behavior in our app | ![]() |
Id connectivity scenario | 1 |
---|---|
Context | When a user is logging in, a request is sent to the database, this has the objective of comparing whether the email and password registered in Firebase Authentication match and in this way being able to return an access key |
Event description | The request cannot be sent and/or received due to problems in the network connection |
System response | When the system does not register the receipt of the key, it confirms the different error cases. The system consults checkInternetConnection, in case of connection failures, it reports the connection problems through a Dialog. If no errors are reported, it will continue with more tests to validate errors in the entry of credentials |
Caching + Retrieving strategy | The access key given by Firebase is cached, this way the login process is faster. |
Storage Type | No clue |
Stored data type | Key value <NPI, NPI> |
Behavior in our app | ![]() |
Id connectivity scenario | 4 |
---|---|
Context | When a user is in the chat menu and is writing a message but runs out of internet or the connection fails before being sent |
Event description | The request cannot be sent due to problems in the network connection |
System response | The application reports connection problems with a dialog, which is why the message cannot be sent until it is online again |
Caching | Conversations are cached loads, so previously loaded chats will still be visible to the user. This allows the user to have access to certain functionalities despite being offline. |
Storage Type | No clue |
Stored data type | Key value <NPI, NPI> |
Behavior in our app | ![]() |
|
Id connectivity scenario | 5 |
---|---|
Context | When a user, being in some of the windows, wants to enter the Chat view without having an internet connection |
Event description | The User cannot Navigate due to problems in the network connection |
System response | The application reports connection problems with a dialog, Conversations are downloaded from the database. That's why the app blocks navigation to this view if the user don't have internet access. |
Storage Type | No clue |
Stored data type | Key value <NPI, NPI> |
Behavior in our app | ![]() |
|
Local storage strategies
We decided that a good strategy for our app would be to use local storage to store the user's information and the items that the app retrieves from the firebase once it is connected to the internet. The application is meant to retrieve and store locally the items that are being sold or interchanged in the app so that the user can see them even if they are not connected to the internet. This is important because it allows the user to see the items that are being sold even if they are not connected to the internet. The implementation of this strategy needs more work and will be improved in the future. One of the most popular ways to implement local storage in a flutter app is to use the shared preferences package. This package allows you to store key-value pairs on the device and retrieve them when needed. Also, there is another possibility to use SQLite to store the data in a database on the device. This is a more complex and less efficient way to store data locally but it is also a possibility. We will continue to work on the implementation of shared preferences to store the user's information and the items that are being sold in the app.
For the Kotlin app, we implemented Multi-threading through the use of coroutines which allow us to manage Long-running tasks (this way we don't block the main thread) and provide Main-safety so that we can call any suspended function from the main thread.
We mainly started coroutines using launch and in the scope of the lifeCycleScope.
References: APA: Napoli, Marco L. 2020. BEGINNING Flutter® A HANDS ON GUIDE TO APP DEVELOPMENT. ISBN: 978-1-119-55082-2
In Flutter, the framework is single-threaded, which means that multithreading is not directly available for concurrent processing. To prevent blocking the main thread and enable the execution of multiple tasks concurrently, Flutter utilizes asynchronous programming with Future functions. Futures represent a block of code that is intended to be executed in the future, allowing operations to run when the main thread is available without causing it to block. Asynchronous operations are extended from Futures to provide the flexibility of releasing the thread when handling high-priority tasks, ensuring efficient utilization of resources and responsiveness in the application.
For example:


The _firebaseService.createMaterialItem(newItem) method returns a Future that represents the asynchronous operation of creating a new item in Firebase database. Also, the then() method in Dart is used to handle the result of a Future once it completes or emits a value (Napoli, 2020). By adding the then() method to the Future, a callback function that will be executed when the Future completes successfully (Napoli, 2020). In the case of the code in the image, the then() method is used to show a success message using a SnackBar when the material item is added successfully to Firebase. It also clears the text controllers and resets the image state for a later use of the widget. On the other hand, the "async" placed before the function declaration indicates that the function is asynchronous. This allows the function to use await within its body to wait for asynchronous operations to complete without blocking the execution of the program (the main and unique main thread) (Napoli, 2020). In the example, the await keyword is used when calling the uploadImage function to upload images asynchronously, pausing the execution of the function until the uploadImage operation is completed, allowing the function to continue its execution after the result is available.
More on Futures:

In our code we use in 10 different files Future related functions or elements. All with the purpose of handling asynchronous operations efficiently. This allows our aplication to run smoothly and perform other tasks while waiting for asynchronous operations to complete.
Finally, the method FutureBuilder allows to define a future whose result will be used to build a widget tree (Napoli, 2020). Meaning that the widget tree will depend on the completion of the asynchronous operation of the future. Also, by declaring the widget with FutureBuilder, the future is constantly monitored by the widget that rebuilds everytime there is change.
In our case, this is used in the allItems view, where the user can visualize all the items that have been posted. The widget where the FutureBuilder is declared depends on the future function that brings all items from firebase. The FutureBuilder also handles connectivity issues and errors regarding the extraction of items from firebase.
FutureBuilder example:


Our cache is temporal, this implies transient data that is periodically deleted by the user, the app, or the OS. Additionally, our app is Online first which means the content is queried first from the Internet and then from the local cache if connection is not available.
Due to the nature of our app being Online first, the caching strategies we implemented are the following:
- Cached on network response**
For the main content in our app, the data is retrieved from the network and duplicated locally on the cache/local storage.
- Cache then network**
from certain info like user images, profile info, and some content, the cached data is shown first, then the data is updated in both the cache and UI when the data from the network arrives.