work in progress
diff --git a/packages/devtools_app/lib/src/screens/app_size/app_size_controller.dart b/packages/devtools_app/lib/src/screens/app_size/app_size_controller.dart
index c8f8669..c7926f2 100644
--- a/packages/devtools_app/lib/src/screens/app_size/app_size_controller.dart
+++ b/packages/devtools_app/lib/src/screens/app_size/app_size_controller.dart
@@ -17,7 +17,7 @@
 import 'app_size_screen.dart';
 
 // Temporary feature flag for deferred loading.
-bool deferredLoadingSupportEnabled = false;
+bool deferredLoadingSupportEnabled = true;
 
 const _artificialRootNodeName = 'ArtificialRoot';
 const _entireAppNodeName = 'Entire App';
@@ -61,6 +61,20 @@
   }
 }
 
+class DiffTreeMap {
+  const DiffTreeMap({
+    required this.combined,
+    required this.increaseOnly,
+    required this.decreaseOnly,
+  });
+
+  final TreemapNode? combined;
+
+  final TreemapNode? increaseOnly;
+
+  final TreemapNode? decreaseOnly;
+}
+
 class AppSizeController {
   static const unsupportedFileTypeError =
       'Failed to load size analysis file: file type not supported.\n\n'
@@ -252,9 +266,13 @@
   ValueListenable<AppUnit> get selectedAppUnit => _selectedAppUnit;
   final _selectedAppUnit = ValueNotifier<AppUnit>(AppUnit.entireApp);
 
-  void changeSelectedAppUnit(AppUnit appUnit) {
+  void changeSelectedAppUnit(AppUnit appUnit, Key tabKey) {
     _selectedAppUnit.value = appUnit;
-    _loadApp(_dataForAppUnit!);
+    if (tabKey == AppSizeScreen.analysisTabKey) {
+      _loadApp(_dataForAppUnit!);
+    } else {
+      print('selected $appUnit in diff view');
+    }
   }
 
   /// Notifies that the json files are currently being processed.
@@ -343,7 +361,8 @@
   ) {
     if (_hasDeferredInfo(jsonFile)) {
       jsonFile['children'] = _extractChildren(jsonFile)
-          .where((child) => child['isDeferred'] == true);
+          .where((child) => child['isDeferred'] == true)
+          .toList();
       jsonFile['n'] = _deferredNodeName;
     }
     return jsonFile;
@@ -385,55 +404,52 @@
     await delayForBatchProcessing(micros: 10000);
 
     Map<String, dynamic> diffMap;
-    if (oldFile.isAnalyzeSizeFile && newFile.isAnalyzeSizeFile) {
-      var oldFileJson = oldFile.data as Map<String, dynamic>;
-      var newFileJson = newFile.data as Map<String, dynamic>;
+    Map<String, dynamic> mainDiffMap;
+    Map<String, dynamic> deferredDiffMap;
 
-      if (_hasDeferredInfo(oldFileJson) || _hasDeferredInfo(newFileJson)) {
+    if (oldFile.isAnalyzeSizeFile && newFile.isAnalyzeSizeFile) {
+      final oldFileJson = oldFile.data as Map<String, dynamic>;
+      final newFileJson = newFile.data as Map<String, dynamic>;
+
+      if (!_hasDeferredInfo(oldFileJson) && !_hasDeferredInfo(newFileJson)) {
+        diffMap = _generateDiffMapFromAnalyzeSizeFiles(
+          oldFileJson: oldFileJson,
+          newFileJson: newFileJson,
+        );
+      } else {
         _isDeferredApp.value = deferredLoadingSupportEnabled;
+        Map<String, dynamic> oldEntireAppFileJson = oldFileJson;
+        Map<String, dynamic> newEntireAppFileJson = newFileJson;
 
         if (!_hasDeferredInfo(oldFileJson)) {
-          oldFileJson = _wrapInArtificialRoot(oldFileJson);
+          oldEntireAppFileJson = _wrapInArtificialRoot(oldFileJson);
         } else if (!_hasDeferredInfo(newFileJson)) {
-          newFileJson = _wrapInArtificialRoot(newFileJson);
+          newEntireAppFileJson = _wrapInArtificialRoot(newFileJson);
         }
-      }
 
-      final oldApkProgramInfo = ProgramInfo();
-      _apkJsonToProgramInfo(
-        program: oldApkProgramInfo,
-        parent: oldApkProgramInfo.root,
-        json: oldFileJson,
-      );
+        final oldMainOnlyFileJson = _extractMainUnit(oldEntireAppFileJson);
+        final newMainOnlyFileJson = _extractMainUnit(newEntireAppFileJson);
 
-      // Extract the precompiler trace from the old file, if it exists, and
-      // generate a call graph.
-      final oldPrecompilerTrace = oldFileJson.remove('precompiler-trace');
-      if (oldPrecompilerTrace != null) {
-        _oldDiffCallGraph = generateCallGraphWithDominators(
-          oldPrecompilerTrace,
-          NodeType.packageNode,
+        final oldDeferredOnlyFileJson =
+            _extractDeferredUnits(oldEntireAppFileJson);
+        final newDeferredOnlyFileJson =
+            _extractDeferredUnits(newEntireAppFileJson);
+
+        diffMap = _generateDiffMapFromAnalyzeSizeFiles(
+          oldFileJson: oldEntireAppFileJson,
+          newFileJson: newEntireAppFileJson,
+        );
+
+        mainDiffMap = _generateDiffMapFromAnalyzeSizeFiles(
+          oldFileJson: oldMainOnlyFileJson,
+          newFileJson: newMainOnlyFileJson,
+        );
+
+        deferredDiffMap = _generateDiffMapFromAnalyzeSizeFiles(
+          oldFileJson: oldDeferredOnlyFileJson,
+          newFileJson: newDeferredOnlyFileJson,
         );
       }
-
-      final newApkProgramInfo = ProgramInfo();
-      _apkJsonToProgramInfo(
-        program: newApkProgramInfo,
-        parent: newApkProgramInfo.root,
-        json: newFileJson,
-      );
-
-      // Extract the precompiler trace from the new file, if it exists, and
-      // generate a call graph.
-      final newPrecompilerTrace = newFileJson.remove('precompiler-trace');
-      if (newPrecompilerTrace != null) {
-        _newDiffCallGraph = generateCallGraphWithDominators(
-          newPrecompilerTrace,
-          NodeType.packageNode,
-        );
-      }
-
-      diffMap = compareProgramInfo(oldApkProgramInfo, newApkProgramInfo);
     } else {
       try {
         diffMap = buildComparisonTreemap(oldFile.data, newFile.data);
@@ -458,27 +474,78 @@
     diffMap['n'] = isDeferredApp.value ? _entireAppNodeName : _rootNodeName;
 
     // TODO(peterdjlee): Try to move the non-active tree generation to separate isolates.
-    _combinedDiffTreeRoot = generateDiffTree(
-      diffMap,
-      DiffTreeType.combined,
-      skipNodesWithNoByteSizeChange: !isDeferredApp.value,
-    );
-    _increasedDiffTreeRoot = generateDiffTree(
-      diffMap,
-      DiffTreeType.increaseOnly,
-      skipNodesWithNoByteSizeChange: !isDeferredApp.value,
-    );
-    _decreasedDiffTreeRoot = generateDiffTree(
-      diffMap,
-      DiffTreeType.decreaseOnly,
-      skipNodesWithNoByteSizeChange: !isDeferredApp.value,
-    );
+    final diffTreeMap = _generateDiffTrees(diffMap);
+    _combinedDiffTreeRoot = diffTreeMap.combined;
+    _increasedDiffTreeRoot = diffTreeMap.increaseOnly;
+    _decreasedDiffTreeRoot = diffTreeMap.decreaseOnly;
 
     changeDiffRoot(_activeDiffRoot);
 
     _processingNotifier.value = false;
   }
 
+  DiffTreeMap _generateDiffTrees(Map<String, dynamic> diffMap) {
+    // TODO(peterdjlee): Try to move the non-active tree generation to separate isolates.
+    return DiffTreeMap(
+      combined: generateDiffTree(
+        diffMap,
+        DiffTreeType.combined,
+        skipNodesWithNoByteSizeChange: !isDeferredApp.value,
+      ),
+      increaseOnly: generateDiffTree(
+        diffMap,
+        DiffTreeType.increaseOnly,
+        skipNodesWithNoByteSizeChange: !isDeferredApp.value,
+      ),
+      decreaseOnly: generateDiffTree(
+        diffMap,
+        DiffTreeType.decreaseOnly,
+        skipNodesWithNoByteSizeChange: !isDeferredApp.value,
+      ),
+    );
+  }
+
+  Map<String, dynamic> _generateDiffMapFromAnalyzeSizeFiles({
+    required Map<String, dynamic> oldFileJson,
+    required Map<String, dynamic> newFileJson,
+  }) {
+    final oldApkProgramInfo = ProgramInfo();
+    _apkJsonToProgramInfo(
+      program: oldApkProgramInfo,
+      parent: oldApkProgramInfo.root,
+      json: oldFileJson,
+    );
+
+    // Extract the precompiler trace from the old file, if it exists, and
+    // generate a call graph.
+    final oldPrecompilerTrace = oldFileJson.remove('precompiler-trace');
+    if (oldPrecompilerTrace != null) {
+      _oldDiffCallGraph = generateCallGraphWithDominators(
+        oldPrecompilerTrace,
+        NodeType.packageNode,
+      );
+    }
+
+    final newApkProgramInfo = ProgramInfo();
+    _apkJsonToProgramInfo(
+      program: newApkProgramInfo,
+      parent: newApkProgramInfo.root,
+      json: newFileJson,
+    );
+
+    // Extract the precompiler trace from the new file, if it exists, and
+    // generate a call graph.
+    final newPrecompilerTrace = newFileJson.remove('precompiler-trace');
+    if (newPrecompilerTrace != null) {
+      _newDiffCallGraph = generateCallGraphWithDominators(
+        newPrecompilerTrace,
+        NodeType.packageNode,
+      );
+    }
+
+    return compareProgramInfo(oldApkProgramInfo, newApkProgramInfo);
+  }
+
   Map<String, dynamic> _wrapInArtificialRoot(Map<String, dynamic> json) {
     json['n'] = _mainNodeName;
     return <String, dynamic>{
diff --git a/packages/devtools_app/lib/src/screens/app_size/app_size_screen.dart b/packages/devtools_app/lib/src/screens/app_size/app_size_screen.dart
index 8ad5fb9..211c38a 100644
--- a/packages/devtools_app/lib/src/screens/app_size/app_size_screen.dart
+++ b/packages/devtools_app/lib/src/screens/app_size/app_size_screen.dart
@@ -208,9 +208,7 @@
                   ),
                   Row(
                     children: [
-                      if (currentTab.key == AppSizeScreen.analysisTabKey &&
-                          isDeferredApp)
-                        _buildAppUnitDropdown(),
+                      if (isDeferredApp) _buildAppUnitDropdown(currentTab.key!),
                       if (currentTab.key == AppSizeScreen.diffTabKey)
                         _buildDiffTreeTypeDropdown(),
                       const SizedBox(width: defaultSpacing),
@@ -253,7 +251,7 @@
     );
   }
 
-  DropdownButtonHideUnderline _buildAppUnitDropdown() {
+  DropdownButtonHideUnderline _buildAppUnitDropdown(Key tabKey) {
     return DropdownButtonHideUnderline(
       key: AppSizeScreen.appUnitDropdownKey,
       child: DropdownButton<AppUnit>(
@@ -265,7 +263,7 @@
         ],
         onChanged: (newAppUnit) {
           setState(() {
-            controller.changeSelectedAppUnit(newAppUnit!);
+            controller.changeSelectedAppUnit(newAppUnit!, tabKey);
           });
         },
       ),