Sprint 4 - mobile-dev-16/S1-G16-planning GitHub Wiki

SPRINT 4 - Wiki Deliverable

Content:

  • Value Proposal

  • Implemented Functionalities

  • Implemented Business Questions

  • Micro-Optimization stragies

  • Eventual Connectivity Solutions

  • Caching Strategies

  • Local Storage Strategies

  • Multi-Threading Strategies

  • Ethics Video


Value Proposal

Our app connects consumers and local businesses to reduce food waste by selling surplus food at affordable prices. For businesses, it provides a platform to turn losses into revenue, enhance their sustainable image, and reach a broader audience. For consumers, it offers access to high-quality meals at lower costs, with personalized offers based on preferences and location, even when offline. From a societal perspective, it contributes to sustainability by reducing carbon emissions and promoting responsible consumption. Supported by key features like personalized notifications, advanced filters, and performance analytics, our solution creates economic and environmental value while fostering user trust and engagement, generating revenue through subscription models, transaction fees, and advertising.

  • User Authentication (Google and Email Sign-In/Sign-Up):

The User Authentication feature ensures that the app is inclusive, secure, and easy to use, aligning with the value proposition of providing a personalized, trusted, and seamless experience. It also supports core functionalities and revenue models by enabling secure user access and engagement.

  • Address Management with GPS and Google Maps API:

Enhances convenience by allowing users to set and manage their location efficiently. This helps them find surplus food deals nearby, directly supporting the promise of accessibility and personalized options. Allows users to define their location precisely, enabling the app to show nearby food surplus deals. This feature makes it easier for users to discover options relevant to their physical location, reducing the effort and time spent searching manually. For businesses, it increases the likelihood of attracting local customers, ensuring surplus food reaches consumers efficiently.By integrating Google Maps API, users can also visualize the distance to a business, adding an intuitive layer of convenience and confidence in their choices.

  • Calculate Loading Time of the App:

Ensures a smooth and efficient user experience by minimizing waiting time. This supports user retention and satisfaction, critical to maintaining engagement and trust in the app. Tracks and optimizes the time taken for the app to load, ensuring that users have a seamless and responsive experience. Faster loading times contribute to user satisfaction, making the app feel polished and professional, which is critical for retaining users. The splash screen sets the tone for the app, while its swift disappearance signals efficient performance, reinforcing trust and encouraging repeat usage.

  • Add and Delete Cart Items:

Simplifies the process of managing orders, making it easier for users to reserve surplus food. This aligns with the app's goal of convenience and user-friendliness. Empowers users to manage their reservations effortlessly, making it simple to add items they want or remove those they no longer need. This functionality supports user convenience, ensuring the process of reserving surplus food is as frictionless as possible. By giving users control over their selections, the app minimizes errors and enhances confidence in completing transactions, increasing the likelihood of deal closures.

  • Scrollable Order Items:

Enhances navigation by allowing users to browse through their orders seamlessly. This improves the overall user experience and encourages repeat usage. Enables users to browse through their order history or current reservations with ease. This feature ensures that all items are visible, even when the list grows, enhancing usability. A well-designed scrolling mechanism ensures users can track their reservations without feeling overwhelmed, supporting a positive user experience and long-term engagement.

  • Profile Information Registration:

Allows users to input and store personal details, creating a foundation for a personalized experience. Information like dietary preferences, favorite cuisines, and saved addresses enhance the app’s ability to offer relevant suggestions. For businesses, knowing more about users enables targeted marketing, such as offering deals that resonate with specific demographics or preferences. This feature fosters user loyalty by making the app feel tailored to their needs.

  • Modifiable Diet and Food Type for Better User Experience:

Tailors the app experience to individual dietary preferences, ensuring users can easily find food options that match their needs, reinforcing the app's value of personalization. Users can update their dietary preferences and favorite cuisines, ensuring the app consistently offers deals that align with their evolving tastes and needs. This flexibility ensures inclusivity, catering to users with specific requirements such as vegetarian, vegan, or allergen-sensitive diets. By keeping the app relevant to user needs, this feature enhances retention and satisfaction, while also encouraging users to explore new offers they might have missed otherwise.

  • "For You" Section Using Favorite Cuisine Type:

Offers a curated list of deals based on user preferences, making the experience more engaging and relevant. This highlights the app's promise of personalized and convenient solutions. Personalizes the app experience by curating surplus food deals based on the user’s favorite cuisine types. This reduces the cognitive load on users, who no longer need to sift through irrelevant options. Encourages exploration and discovery by showcasing deals that align with preferences, driving user engagement and satisfaction. Businesses benefit from increased visibility when their offers match user interests, creating a win-win scenario for both parties.

  • Log Out Option:

Provides users with control over their accounts, ensuring a secure and trustworthy platform, which is vital for maintaining long-term user engagement. Provides users with control over their accounts, ensuring their data and session are secure. This feature is essential for building trust, especially for apps handling sensitive information like payment details or location data. A simple, reliable log-out mechanism shows users that their privacy is a priority, which is critical for retaining trust over time.

  • User Information in Cache:

Supports offline functionality by allowing users to access their information even without internet connectivity. This ensures a consistent experience, reinforcing reliability and trust. Stores essential user data locally, ensuring the app remains functional even when the user is offline. This guarantees a smooth experience regardless of connectivity issues. By caching data such as preferences and recently viewed deals, the app minimizes frustration and increases usability, particularly for users in areas with unreliable internet. For businesses, cached data ensures their deals are visible to users at all times, maximizing potential sales.

  • Flutter Toast to Manage Eventual Connectivity:

Keeps users informed of their connectivity status in real time with clear visual cues. For instance, a green notification indicates connectivity, while a red one warns of disconnection. By proactively notifying users, the app builds trust, ensuring users know what to expect when offline. This transparency prevents confusion and enhances the overall experience. Businesses benefit as users are less likely to abandon transactions due to connectivity uncertainties.

  • Complete Cart Items Management:

Allows users to calculate the total cost, add, delete, or clear items from their cart, simplifying the reservation process. This ensures ease of use and encourages order completion. Provides comprehensive tools for users to manage their cart, including calculating the total order cost, modifying items, or clearing the cart entirely. This functionality streamlines the reservation process, reducing errors and ensuring users feel in control of their purchases. By simplifying the cart management process, the app encourages users to complete transactions, benefiting both users and businesses.

  • List of Available Restaurants:

Increases user engagement by providing a comprehensive list of nearby restaurants offering surplus deals. This supports the app's mission to connect users with local businesses and reduce food waste. Displays nearby restaurants offering surplus food, increasing the visibility of partner businesses and helping users discover new options. This list serves as a centralized hub for surplus food deals, reinforcing the app’s value proposition of connecting users with local businesses to reduce waste. The feature supports both user engagement and business reach, making it easier for users to find and support eco-friendly establishments.

List of implemented Functionalities

Sprint 2

  • User Authentication (Google and Email Sign-In, Sign-Up).
  • Address Management with GPS and Google Maps API.
  • Calculate loading time of the app from launch to the main page and loading with splash.
  • Add and delete cart items.
  • Scrollable order items.

Sprint 3

  • Profile information registration.
  • Modifiable diet and food type for better user experience
  • "For you" uses user favorite cuisineType
  • Log out option
  • User information in cache
  • Flutter toast to manage Eventual Connectivity
  • Complete cart items management.
  • calculate total order and delete it completely
  • List of available restaurants

Sprint 4

  • Help and support form
  • Set profile image
  • Restaurant review
  • Placer order
  • Order again
  • Order detail

Implemented Business Questions

Sprint 2

  • What is the average loading time of the app from launch to the main page? (Type 1) ( Raul )
  • Is there a surplus of your favorite cuisine available today near your location? (Type 2) ( Abel )
  • Is the user currently located far from their saved address? (Type 2) ( Raul )

Sprint 3

  • How does the app's performance (loading times, crash rates) correlate with user retention rates? (Type 5) ( Raul )
  • Are there any new partner businesses offering surplus deals near the user's saved address today? (Type 2) ( Mario )
  • Are there new surplus food deals available that match the user’s dietary preferences today? (Type 2) ( Abel )

Sprint 4

  • Which app features are used less than once per week by active users? (Type 3)
  • How frequently do users use the "Order Again"? (Type 3)
  • Which types of support tickets are most frequently generated in the application? (Type 3)
  • At what times do users most frequently buy surplus food, and can we sell this non-personal aggregated data to logistics companies to optimize delivery services? (Type 4)

Profiling

Overdrawing

image image image image image image image image image image image

Our app design is optimized to ensure smooth performance without unnecessary layering or rendering inefficiencies. Below are the key aspects of how we achieve this:


1. Background and Base Layers

  • The backgroundColor property in the global Scaffold is used across all screens, eliminating redundant containers or overlays.
  • Opaque elements are structured efficiently without stacking unnecessary layers.

2. Text Elements and Widgets

  • Text widgets, such as labels, are integrated seamlessly into layouts without additional wrapping or redundant background properties.
  • Semi-transparent effects are achieved directly through parent containers with BoxDecoration and applied opacity.

3. Interactive Elements (Buttons, Rating Sliders, and Dropdowns)

  • Material widgets are utilized directly, with shadows and elevation only where visually essential.
  • Sliders and inputs are implemented without excessive wrapping, ensuring clean and efficient layering.

4. Images and Banners

  • Images are rendered in single containers with efficient scaling methods like BoxFit.cover or BoxFit.contain.
  • Layers like Stack or ClipRRect are only used where required for design elements.

5. Address Selection Map

  • The map widget integrates seamlessly with a single parent container, minimizing overlapping layers.
  • Lazy loading is applied for markers and overlays to optimize performance.

6. Profile Screen - Image Picker

  • Lightweight dialog overlays are used, with minimal shadows and simple background properties to enhance efficiency.

7. Support Request Screen

  • List items are designed using ListTile components without unnecessary elevation or shadows.
  • Dividers are utilized for clear separation without additional layers.

UI

image image

Here showcases a Flutter widget tree structure of the EcoBitesApp, which demonstrates the heavy usage of MultiBlocProvider, BlocProvider, and BlocListener. This setup is indicative of a strongly decoupled architecture where state management is handled at various layers of the application. The highlighted section in the image represents a CarouselView wrapped within a ConstrainedBox. This ConstrainedBox defines the dimensions for the CarouselView, but the absence of additional constraints leads to a potentially unoptimized layout.

The main concern here is unnecessary nesting and overly deep widget hierarchies. The multiple layers of BlocProvider and listeners might increase rendering time and memory usage. Additionally, the CarouselView appears to have an unconstrained height, which might result in improper scaling or layout issues when combined with other widgets. The lack of explicit constraints in the design could lead to redundant rendering passes, which might affect the overall performance of the app.

The anothehr one focuses on a specific portion of the layout within the HomeScreenContent, particularly showcasing the structure of a Row widget used within a TabBar. Each tab includes an Icon, a SizedBox (for spacing), and a Text widget. The highlighted Row reveals details about the layout parameters for each child, such as their constraints, flex properties, and alignment.

A notable observation is that the SizedBox within the Row has unconstrained horizontal dimensions, resulting in unnecessary use of loose constraints. This might contribute to wasted layout computation or render passes, especially in scenarios with dynamic content or varying screen sizes. Furthermore, the overuse of SizedBox for spacing between Icon and Text could potentially be replaced with padding or margin adjustments to simplify the layout.

Lastly, while the layout ensures alignment and proper spacing for the content, the use of nested rows and columns in a constrained environment may lead to inefficiencies. This inefficiency might manifest as unnecessary overdraws during rendering, especially if these rows are part of a larger ListView or ScrollView. Optimizing this section could reduce layout complexity and improve rendering performance for a smoother user experience.

image image

The layout in the first image focuses on a Row widget nested within an ExpansionTile that contains multiple child elements such as a CircleAvatar, Text widgets, a Spacer, and TextButton. The Row is centered along the Cross Axis while maintaining loose constraints for the horizontal direction. The key layout elements include:

  • Text Widget: The primary Text widget displays labels such as "Bogotá Beer Company" or "Hornitos." The width of the Text widget is dynamically determined based on its content, resulting in variable widths like 192.3 pixels.
  • Padding: Adjacent to the Text widget, the Padding ensures spacing around the Text and other elements.
  • Spacer: A flexible spacer allows for dynamic distribution of remaining space between the text content and the TextButton. This ensures that the TextButton aligns to the far-right of the row.
  • TextButton: Positioned at the end of the row, this button is rendered with a fixed width of 64.0 pixels and height of 48.0 pixels. Its placement ensures visual clarity and interactivity.

The layout is efficient but could benefit from additional constraints to handle edge cases such as extremely long text labels that might truncate or overflow, as indicated by the width is unconstrained warning.

  • Nested Rows and Spacers: The Row widget includes a Text and a Spacer, followed by another child (e.g., TextButton). The Spacer dynamically adjusts spacing to align elements symmetrically.
  • Height Discrepancies: A highlighted issue is the combined height of the child elements exceeding the parent’s height constraints (children take 307.3 while the allocated height is 243.4). This could lead to rendering inefficiencies or visual artifacts like clipping or overflow.
  • Main Axis Layout: The layout aligns elements horizontally (Main Axis) while maintaining a flexible arrangement along the Cross Axis. Elements like Padding ensure consistent spacing between the Row and other components.

To optimize the layout, the constraints for parent containers such as the ExpansionTile or Row need to be adjusted. Adding explicit height constraints or utilizing Flexible widgets instead of fixed spacers could help resolve the height mismatch. Additionally, wrapping text in containers with ellipses truncation could handle long content gracefully.

image image

The first visualization demonstrates the structure of a Row widget containing an image, a SizedBox, and an expanded section. The layout emphasizes flexibility in allocating horizontal space. The Expanded widget dynamically adjusts to fill the remaining width, while the SizedBox adds spacing between the image and text. This configuration supports adaptability in different screen sizes but can be optimized further to reduce rendering overhead caused by unconstrained heights and loose fits, which could lead to inefficiencies during layout calculations.

In the second visualization, the Column widget contains textual elements styled within containers and padded for spacing. The hierarchical layout is clear, with a structured presentation of order details, like restaurant names and statuses. The SizedBox serves as a spacing mechanism, and the InkWell provides interactivity for tapping on individual orders. While functional, the layout might benefit from tighter constraints to enhance rendering efficiency and reduce potential overdraw or redundant space allocations.

image image

In the first layout, the DropdownButtonFormField is displayed as part of a Column within a structured vertical layout. This widget is responsible for letting users select a specific type of cuisine from a predefined list. The DropdownMenuItems for each cuisine type, such as "Local," "Italian," and "Mexican," are rendered dynamically. The layout contains proper Padding elements around the text fields and dropdown, ensuring visual spacing between components. However, the SizedBox within this view introduces unnecessary constraints with a zero width, which could be removed to optimize the rendering. This adjustment would streamline the hierarchy and reduce the layout's complexity.

Additionally, the presence of multiple layers of Padding could be consolidated, as this redundancy may increase overdraw and affect performance. Aligning the DropdownButtonFormField to the full width of the parent container, along with simplifying spacing, would enhance the user experience and application performance.

In the first layout, the DropdownButtonFormField is displayed as part of a Column within a structured vertical layout. This widget is responsible for letting users select a specific type of cuisine from a predefined list. The DropdownMenuItems for each cuisine type, such as "Local," "Italian," and "Mexican," are rendered dynamically. The layout contains proper Padding elements around the text fields and dropdown, ensuring visual spacing between components. However, the SizedBox within this view introduces unnecessary constraints with a zero width, which could be removed to optimize the rendering. This adjustment would streamline the hierarchy and reduce the layout's complexity.

Additionally, the presence of multiple layers of Padding could be consolidated, as this redundancy may increase overdraw and affect performance. Aligning the DropdownButtonFormField to the full width of the parent container, along with simplifying spacing, would enhance the user experience and application performance.

The second image depicts a Row layout within a Card widget under a ListView. Inside the Row, two main Text widgets display the status and date of an order, such as "Pending" and "2 Dec." These elements are encapsulated within a Container, providing some styling and spacing. The row maintains flexibility by aligning the components horizontally. However, similar to the first layout, a SizedBox with zero width introduces redundant layout calculations that can be removed to simplify the widget tree.

The horizontal alignment appears constrained due to fixed widths, such as the Container width of 84.1 pixels. Adjusting this to dynamically resize based on available space would make the layout more responsive and visually appealing. Moreover, ensuring the Text widgets fit well within their respective containers without clipping or overflow would contribute to a cleaner UI. Lastly, reducing nesting within the Card could optimize rendering and improve overall performance.

Performance

image

The performance graph for the Profile View in our application indicates multiple "Jank" occurrences (red bars), which signal frames that took longer than the desired 16ms (for 60 FPS) or 11ms (for 90 FPS) to render. This suggests that the UI is experiencing noticeable delays, leading to a less smooth user experience.

The primary contributors to these performance issues likely stem from unnecessary rebuilds or excessive work being performed during the rendering process of this screen. Specifically:

  1. Complex Widget Hierarchy: The Profile View includes several TextFields, DropdownButtons, and a ListView. Each of these widgets involves layout calculations and rendering, which, when combined, can significantly increase the workload during frame updates.

  2. Rebuilding on State Changes: If the widgets in this screen are being rebuilt unnecessarily, such as when the state updates even slightly, it can increase the rendering time for each frame. For example, a change in the BlocListener or a rebuild triggered by input fields might cause a chain reaction that re-renders the entire screen rather than isolated components.

  3. Dynamic Elements: Widgets such as DropdownButton and user-input fields might involve event handling and dynamic rendering that requires re-layout and re-painting, increasing the computational cost of each frame.

  4. Inefficient State Management: If the state management is not optimized (e.g., updating the entire widget tree instead of specific parts), it can lead to overdraw and additional rendering time.

image

The performance graph for the rest of the application exhibits consistent frame rendering times, primarily represented by blue bars corresponding to the UI Frame Time and Raster Frame Time. There are no visible "Jank" bars (red), indicating that the application maintains a steady frame rate and provides a smooth user experience across its various screens. The average FPS is 81, which is a positive indication for performance.

Key Observations:

  1. Stable Frame Times: The frame rendering times remain below the threshold of 16ms (for 60 FPS) or 11ms (for 90 FPS), indicating efficient rendering without significant delays.

  2. No Shader Compilation Delays: The absence of red bars related to shader compilation suggests that the application has precompiled shaders effectively or does not introduce heavy graphical changes that require new shader generation.

  3. Consistent UI Updates: The blue bars reflect minimal variance in frame times, signifying that the application optimally handles UI updates without overloading the rendering pipeline.

  4. Well-Optimized Screens: Unlike the Profile View, the rest of the application avoids unnecessary rebuilds or heavy computational tasks during the rendering cycle, keeping frame times consistent.

Likely Causes of This Performance:

  • Efficient Widget Design: The use of lightweight widgets and proper management of state across the app reduces unnecessary re-layouts and re-painting.
  • Optimized State Management: Techniques such as BlocBuilder and efficient dependency injection ensure only necessary parts of the widget tree are rebuilt during state changes.
  • Minimal Overdraw: Proper layering and avoidance of excessive overlapping widgets contribute to a cleaner rendering pipeline.

Analysis of CPU Usage and Threading in the Application

image image

CPU Usage Analysis

  1. High Frame Rendering Overhead:

    • A significant portion of CPU time is consumed by the SchedulerBinding methods, such as handleDrawFrame and invokeFrameCallback. These methods are core Flutter mechanisms for managing the UI pipeline and ensuring frames are rendered within the allocated 16ms per frame to maintain 60 FPS.
    • The drawFrame method alone accounts for a substantial percentage of the CPU usage, indicating that rendering operations are CPU-intensive.
  2. Rendering and Painting Bottlenecks:

    • Painting operations, such as PaintingContext.paintChild and RenderObject.paintWithContext, consume considerable CPU resources. This suggests complex widget structures or excessive widget rebuilding during each frame.
    • Methods related to compositing (repaintCompositedChild) and layout recalculations indicate that the app frequently recalculates widget positions and repainting layers, which is expensive in terms of CPU cycles.
  3. Widget Rebuilding:

    • Element.rebuild and ComponentElement.performRebuild suggest frequent widget tree rebuilds. While necessary for dynamic UI updates, this could indicate inefficient state management or a lack of optimizations in widget rebuild triggers.

Threading Observations

  1. Main Thread Saturation:

    • Most operations, including frame scheduling, layout calculations, and rendering, are handled on the main thread. This is evident from the consistent CPU usage spikes during drawFrame and handleDrawFrame.
    • Since Flutter heavily relies on the main thread for rendering, saturation can lead to dropped frames (jank) if the main thread cannot keep up with the 16ms frame rendering budget.
  2. Absence of Thread Offloading:

    • There is limited evidence of offloading CPU-intensive tasks (e.g., painting or layout recalculations) to background threads. Offloading such tasks to isolate threads could alleviate main thread bottlenecks and improve performance.
  3. Rendering and Build Task Overlap:

    • The flame chart reveals overlapping tasks in the rendering pipeline. This suggests suboptimal scheduling of tasks where rendering and widget rebuilding compete for CPU resources on the same thread.

The profiling data highlights a need for optimizations in widget rebuilding, rendering, and threading. By adopting targeted optimizations, such as reducing widget rebuilds, introducing thread offloading, and simplifying the layout structure, the application can significantly improve its CPU efficiency and overall performance.

Memory management

image image image

Memory Usage Overview

  1. Resident Set Size (RSS):

    • RSS is around 392.45 MB, indicating the total memory allocated by the app, including the native heap and Dart/Flutter memory. This is relatively high, suggesting potential optimization opportunities in asset loading, widget management, or background processes.
  2. Allocated Memory:

    • The allocated memory is 153.64 MB, which is the live memory managed by Dart/Flutter. A significant portion of this is due to Dart/Flutter Native at 132.76 MB and Dart/Flutter at 17.23 MB. Native memory usage might stem from operations like image decoding or native libraries.
  3. Garbage Collection (GC):

    • GC events are represented by purple markers in the timeline. The frequent GC operations imply that memory is actively being reclaimed, which can help prevent memory leaks. However, excessive GC could indicate issues like high object churn (e.g., creating too many short-lived objects).
  4. Top Memory Consumers:

    • OfferModel leads with 28 instances, using 1.8 KB in Dart Heap. This model is likely related to business or UI logic. Its memory usage appears efficient but should be monitored for scaling.
    • Elements like _InheritedProviderScopeElement and _NestedHookElement show lower instance counts and memory usage but are critical as they manage state and reactivity.
  5. BlocProviders:

    • Several BlocProviders and their associated state classes have minimal memory usage, which is a good sign for state management efficiency. However, the total number of providers (10 instances for one provider) suggests that further consolidation might optimize memory.
  6. Garbage Collection Statistics:

    • Latency: The GC latency is around 8 ms, which is acceptable for smooth performance. However, collections are frequent (e.g., 138 total collections for 15.8 MB capacity), showing that GC is actively managing memory.
    • Dart Heap Usage: Dart heap usage for most classes stays below 1 MB, which is expected for efficient data structures and widget hierarchies.

Micro-optimization strategies

Added database indexes for better query performance in local data source

image

Indexes on Columns (CREATE INDEX): Indexing columns like category and cuisineType in your food_businesses table optimizes query performance. This is especially beneficial for operations like filtering or searching, as the database can quickly locate rows matching the criteria instead of scanning the entire table. The result is faster data retrieval, particularly in scenarios where these columns are heavily queried, such as displaying businesses based on categories or cuisines.

Added buildWhen and listenWhen to prevent unnecessary rebuilds

image image

buildWhen in BlocBuilder: The buildWhen condition ensures the UI rebuilds only when specific changes occur in the state. In this case, the BlocBuilder rebuilds only if the runtimeType of the state changes or if the list of foodBusinesses updates. This reduces unnecessary widget rebuilds, improving rendering performance and avoiding redundant UI updates when unrelated state changes occure.

listenWhen in BlocListener: The listenWhen condition ensures that the BlocListener responds to state changes only when relevant. Here, it listens for transitions into an AddressLoaded state while ignoring other state updates. This approach avoids executing callback logic unnecessarily, improving application responsiveness and resource usage.

Added widget recycling with keys

image image

Keying Widgets with ValueKey: Using a ValueKey for widgets ensures that the framework efficiently differentiates widgets in a list. For example, the key generated from the foodBusiness.id and dietType uniquely identifies each widget, allowing the framework to reuse existing widgets during updates instead of recreating them. This reduces the overhead of rebuilding widgets and enhances scrolling performance in lists. Similarly, we assig ValueKey to OfferCard widgets ensures that updates to the list do not cause the framework to rebuild or recreate the entire widget tree unnecessarily. This approach is particularly useful for large, dynamic lists like offers, where maintaining smooth UI interactions and avoiding unnecessary computations are critical.

image image

In this results we demonstrate significant improvements in both frame rendering and memory management within the app. Frame rendering shows consistent times below the 16ms threshold, ensuring smooth navigation and interactions. The absence of jank (slow frames) indicates optimized layouts and efficient widget rebuilds, leading to a responsive and seamless user interface. These improvements directly enhance user experience by eliminating lag and maintaining steady performance.

In terms of memory usage, the allocated memory is well-managed at 120.81 MB, with efficient garbage collection cycles ensuring no memory bloat or leaks. The stable RSS (393.96 MB) reflects controlled resource usage, reducing strain on devices and improving energy efficiency. These optimizations, achieved through strategies like database indexing, state management (buildWhen, listenWhen), and efficient widget differentiation using ValueKey, contribute to a scalable, resource-efficient, and high-performing application.

Eventual Connectivity Solutions

image

The getOrders function integrates eventual connectivity by first checking the network status. If online, it fetches orders from the remote data source, caches them locally, and returns them. In case of an authentication exception, it retrieves orders from the local cache as a fallback. If offline, it directly attempts to fetch orders from the cache. If the cache retrieval fails, it emits a network failure state. This layered approach ensures data availability even under network constraints.

image

The updateOrderStatus function checks connectivity before performing any updates. If online, it updates the order status both in the remote data source and the local cache to maintain synchronization. If offline, it returns a network failure, preventing data inconsistency. By separating the logic for connected and disconnected states, the function ensures that operations remain accurate and reliable regardless of network conditions.

image

This function ensures ticket submission under various connectivity scenarios. When the user submits a ticket, the function attempts to add the ticket data (category, subOption, reason, and description) to Firestore. If the operation succeeds, a SupportSuccess state is emitted. However, if there's a failure—likely due to no internet connectivity—the ticket is cached locally using _cacheTicket for later submission, and a SupportCached state is emitted with a message to notify the user that the ticket will be submitted once the internet connection is restored. This mechanism ensures that users can continue their actions without disruption, even in offline scenarios.

image

In this one retrying the submission of tickets that were cached due to connectivity issues. It retrieves cached ticket data from SharedPreferences, iterates over each cached ticket, and attempts to add it to Firestore. If successful, the cached tickets are removed from storage, and a SupportSuccess state is emitted. If any error occurs during the process, a SupportFailure state is triggered with the corresponding error message. This ensures that previously failed submissions are resolved once connectivity is restored, maintaining consistency.

Caching Strategies

image

The _pickImage method allows the user to select an image from their device's gallery and saves it to a temporary cache directory. First, it requests storage permission using the Permission.storage API. If permission is granted, the ImagePicker library is used to open the gallery and allow the user to choose an image. Once an image is selected, it updates the state of _profileImage with the file path of the chosen image, ensuring the UI reflects this change. Next, it saves the image to a temporary directory using getTemporaryDirectory and creates a copy of the image in a predefined path, such as profile_image.png. If the storage permission is denied or an error occurs during the image selection or saving process, appropriate messages are printed to the console for debugging purposes. This implementation ensures a smooth user experience while handling necessary permissions and temporary caching of profile images.

Local Storage Strategies

image

The cacheOrders function is designed to store a list of orders (OrderModel) and their associated items (OrderItem) into a local SQLite database. This approach ensures that the cached data remains up-to-date and consistent with the latest application state. It is particularly useful in offline-first applications, where local storage acts as a fallback when the app cannot connect to a remote server.

The function begins by obtaining an instance of the SQLite database. Once the database instance is retrieved, a transaction is initiated to group all database operations into a single atomic block. This guarantees that either all operations (deleting old data and inserting new data) are executed successfully, or none are applied if an error occurs, ensuring data consistency.

The first step inside the transaction is to clear any existing data from the orders and order_items tables. This is achieved using the txn.delete method, which ensures that the cache only holds the latest data, avoiding any duplication or outdated records. Once the tables are cleared, the function loops through the provided list of orders.

For each order, the function inserts the order's details into the orders table. This includes fields like the order ID, business name, total amount, status, and timestamps (converted to ISO 8601 format for consistency). The ConflictAlgorithm.replace is used to handle cases where an order with the same ID already exists in the database, replacing it with the new data to maintain data integrity.

After storing the order, the function iterates through its associated items and inserts them into the order_items table. Each item includes detailed information such as the item ID, product ID, product name, quantity, and price. Like the orders, the items are also stored using ConflictAlgorithm.replace to avoid duplicate entries and ensure the latest information is preserved.

The entire process is wrapped in a try block to handle any potential errors. If an error occurs during the transaction, it is caught, and appropriate error-handling mechanisms can be applied, such as logging or retrying the operation.

Overall, this function is a robust and efficient method for caching order-related data locally. It ensures the app can operate seamlessly in offline scenarios, provides atomicity through transactions, and uses conflict resolution strategies to maintain data consistency and integrity. This makes it an essential part of an offline-first strategy in modern mobile app development.

image

This code handles fetching order data, prioritizing remote retrieval while falling back on cached data in case of failures. If an AuthException occurs (e.g., authentication-related issues), it logs the error and attempts to retrieve cached orders from the local data source. If no cached data is available, it returns an authentication failure message. Similarly, if a general network issue occurs, the code tries to fetch cached data and, if unsuccessful, returns a network failure message indicating the absence of an internet connection. This design ensures the app remains functional even in offline scenarios by leveraging local data storage as a fallback mechanism.

image

This code defines a method _cacheTicket that saves a support ticket to local storage using SharedPreferences. It retrieves the existing list of cached tickets (stored as JSON strings) or initializes an empty list if none exist. The new ticket is encoded as a JSON object with details such as category, sub-option, reason, and description, and is added to the list of cached tickets. Finally, the updated list is stored back into SharedPreferences under the key cachedTickets. This approach ensures that user-generated tickets are temporarily saved locally when an internet connection is unavailable, allowing them to be resubmitted automatically once the connection is restored.

Multi-Threading Strategies

image

Here we demonstrates the use multithreading strategy using Dart’s asynchronous capabilities (async/await) to manage ticket submissions in a non-blocking and user-friendly manner. The _onSubmitTicket function is designed to handle the potentially lengthy process of uploading a ticket to Firestore, ensuring the app remains responsive even when the operation involves network delays or failures.

The function starts by emitting a SupportLoading state, signaling to the UI that a ticket submission is in progress. This is particularly useful for updating the user interface, such as disabling buttons or showing a loading indicator.

Next, the function attempts to submit the ticket to Firestore using an await call. This ensures that the Firestore operation is completed before moving to the next line of code, but without freezing the main thread. If the operation is successful, the function emits a SupportSuccess state, informing the application and the user that the ticket has been submitted successfully.

If the Firestore operation fails (e.g., due to a lack of internet connectivity), the function enters the catch block. Here, it invokes the _cacheTicket method to store the ticket locally using a caching mechanism, such as SharedPreferences. This allows the ticket to be saved temporarily until the device regains connectivity. After caching the ticket, the function emits a SupportCached state, which includes a message to inform the user that their ticket has been saved and will be automatically submitted when the internet connection is restored.

This strategy ensures that the user experience is seamless and robust. Even in offline scenarios, users are reassured that their actions are not lost. By leveraging asynchronous programming, the app handles time-consuming tasks efficiently, avoiding UI freezes or unresponsiveness. Additionally, the use of state management ensures clear communication between the business logic and the UI, enabling the app to provide feedback in real-time, whether the ticket submission succeeds or needs to be deferred.

Ethics Video (Click below)

https://youtu.be/biKU7HLSal0