<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Hrushikesh Desai Logs]]></title><description><![CDATA[Hrushikesh Desai Logs]]></description><link>https://hrushikeshdesai.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/6ae74614e12b8f006f761487/a519a8cf-b13d-45d5-ab15-8727e7c3c0f8.png</url><title>Hrushikesh Desai Logs</title><link>https://hrushikeshdesai.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 08 Nov 2026 08:15:01 GMT</lastBuildDate><atom:link href="https://hrushikeshdesai.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How We Reduced Our Flutter + Unity App Size from 180MB to 34MB]]></title><description><![CDATA[Integrating Unity into a Flutter application was straightforward initially. The real challenge started when we looked at the final application size. Our…]]></description><link>https://hrushikeshdesai.hashnode.dev/how-we-reduced-our-flutter-unity-app-size-from-180mb-to-34mb</link><guid isPermaLink="true">https://hrushikeshdesai.hashnode.dev/how-we-reduced-our-flutter-unity-app-size-from-180mb-to-34mb</guid><dc:creator><![CDATA[HRUSHIKESH DESAI]]></dc:creator><pubDate>Sun, 01 Nov 2026 14:35:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae74614e12b8f006f761487/0cbb39bd-ca4c-45c5-aa4f-d0d93cc8718b.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Integrating Unity into a Flutter application was straightforward initially. The real challenge started when we looked at the final application size.</p>
<p>Our Android build was around 180MB.</p>
<p>A large part of it came from Unity's runtime, native libraries and bundled assets. We were shipping almost everything with the application, even though users didn't need all those resources when they first opened it.</p>
<p>We didn't want to compromise the 3D experience just to reduce the app size. So instead of spending more time on basic compression, we decided to revisit the architecture.</p>
<p>We eventually brought the initial application package down from 180MB to 34MB, an approximately 81% reduction.</p>
<p>Here's what we changed.</p>
<ol>
<li>Getting the Architecture Right</li>
</ol>
<p>We were using Flutter for the main application and Unity for the 3D experience. I wanted to keep both reasonably independent rather than mixing Unity-related logic throughout the Flutter codebase.</p>
<p>We followed Clean Architecture with three main layers:</p>
<p>Presentation: Flutter screens, BLoC, events and states.</p>
<p>Domain: Use cases, repository contracts and business rules.</p>
<p>Data: API integration, local storage, asset downloads and repository implementations.</p>
<p>We used flutter_bloc for state management.</p>
<p>This was particularly useful for workflows like Unity initialization, asset downloads and local synchronization, where multiple asynchronous operations were involved.</p>
<p>The UI only needed to know the current state. It didn't need to know how an asset was downloaded, verified or stored.</p>
<ol start="2">
<li>Integrating Unity with Flutter</li>
</ol>
<p>We used unity_flutter to integrate the Unity experience into the Flutter application.</p>
<p>One thing we had to be careful about was communication between the two environments.</p>
<p>Flutter was responsible for the application UI, navigation and business logic. Unity handled the 3D rendering and Unity-specific operations.</p>
<p>We also worked with native platform channels to handle communication and lifecycle-related operations.</p>
<p>Our communication layer supported:</p>
<p>Sending commands from Flutter to native/Unity.</p>
<p>Receiving asynchronous events and status updates.</p>
<p>Handling Unity initialization and lifecycle changes.</p>
<p>Keeping communication independent of individual screens.</p>
<p>We used method-based communication for commands and event streams for asynchronous updates.</p>
<p>This helped us keep the integration manageable without coupling the entire Flutter application to Unity.</p>
<ol start="3">
<li>Getting Unity Assets Out of the Initial Download</li>
</ol>
<p>This was probably the most important change.</p>
<p>Initially, we were shipping too many Unity resources as part of the application package.</p>
<p>We changed that approach and moved towards remote asset delivery using Unity AssetBundles.</p>
<p>Instead of including every resource in the initial build, we hosted assets remotely and downloaded them when required.</p>
<p>The general flow looked like this:</p>
<p>Fetch asset metadata and version information.</p>
<p>Check which assets are already available on the device.</p>
<p>Identify missing or outdated assets.</p>
<p>Download the required bundles.</p>
<p>Verify the downloaded files.</p>
<p>Make the assets available to Unity.</p>
<p>We also used LZ4 compression to manage asset size and loading performance.</p>
<p>Where needed, we handled downloads in smaller chunks rather than treating all resources as one large operation.</p>
<p>This reduced the amount of content that needed to be included in the initial application package.</p>
<ol start="4">
<li>Managing Assets on the Device</li>
</ol>
<p>Remote downloads introduced another problem. We couldn't download the same assets every time a user opened the Unity experience.</p>
<p>We needed a proper local synchronization mechanism.</p>
<p>We added an asset-management layer that handled local availability, version checks and download status.</p>
<p>The basic idea was simple:</p>
<p>Check local assets first.</p>
<p>Compare their versions with remote metadata.</p>
<p>Download only missing or outdated resources.</p>
<p>Verify downloaded files before accepting them.</p>
<p>Reuse existing assets wherever possible.</p>
<p>This also helped us avoid unnecessary network requests.</p>
<p>We kept this logic outside the UI and exposed the synchronization status through BLoC.</p>
<p>That made it easier to handle loading states, failures and retry operations without adding more complexity to the screens.</p>
<ol start="5">
<li>Asset Integrity</li>
</ol>
<p>When assets are downloaded remotely, validating them before loading is important.</p>
<p>We added verification to the download workflow so that incomplete or modified files wouldn't be accepted blindly.</p>
<p>We also used HMAC-based verification as part of our asset integrity approach.</p>
<p>The downloaded content was validated before being made available to the application.</p>
<p>This was particularly important because we were no longer shipping all resources inside the original application package.</p>
<ol start="6">
<li>Keeping Flutter and Unity Responsive</li>
</ol>
<p>Moving assets to remote delivery solved the package-size problem, but it also meant we had to pay more attention to runtime behaviour.</p>
<p>We didn't want asset downloads, file operations or communication between Flutter and Unity to interfere with the rendering experience.</p>
<p>We kept expensive operations away from the UI thread, handled asynchronous work separately and paid attention to Unity's lifecycle and memory management.</p>
<p>Our target was to maintain a smooth 60 FPS experience while keeping asset loading and synchronization under control.</p>
<ol start="7">
<li>The Results</li>
</ol>
<p>The biggest improvement was in the initial application size.</p>
<table>
<thead>
<tr>
<th>Metric</th>
<th align="right">Before</th>
<th align="right">After</th>
</tr>
</thead>
<tbody><tr>
<td>Application package size</td>
<td align="right">~180 MB</td>
<td align="right">~34 MB</td>
</tr>
<tr>
<td>Size reduction</td>
<td align="right">—</td>
<td align="right">~81%</td>
</tr>
</tbody></table>
<p>More importantly, we no longer needed to ship every Unity resource with the initial application.</p>
<p>Assets could be downloaded, verified, cached and synchronized independently.</p>
<p>Final Thoughts</p>
<p>One thing I learned from this implementation is that reducing application size isn't always about removing dependencies or compressing assets.</p>
<p>Sometimes the better solution is to change what you ship in the first place.</p>
<p>Flutter and Unity can work well together, but they need a clear separation of responsibilities. Asset delivery, native communication, local storage and lifecycle management should all be considered when designing the integration.</p>
<p>This was a good reminder that a small architectural change can make a much bigger difference than several rounds of build optimization.</p>
<p>If you're working with Flutter and Unity, I'd love to hear how you're handling similar challenges you can reach me out by - <a href="http://hrushikeshdesai.com">hrushikeshdesai.com</a></p>
]]></content:encoded></item></channel></rss>