Cleanup code after retirement of the Analysis tab. (#4733)

diff --git a/packages/devtools_app/lib/src/screens/memory/memory_analyzer.dart b/packages/devtools_app/lib/src/screens/memory/memory_analyzer.dart
deleted file mode 100644
index 9b2d310..0000000
--- a/packages/devtools_app/lib/src/screens/memory/memory_analyzer.dart
+++ /dev/null
@@ -1,721 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:collection';
-
-import 'package:flutter/material.dart';
-import 'package:vm_service/vm_service.dart';
-
-import '../../primitives/auto_dispose_mixin.dart';
-import '../../primitives/utils.dart';
-import '../../shared/table/table.dart';
-import '../../shared/table/table_data.dart';
-import '../../shared/utils.dart';
-import 'memory_controller.dart';
-import 'memory_graph_model.dart';
-import 'memory_heap_tree_view.dart';
-import 'memory_snapshot_models.dart';
-
-bool _classMatcher(HeapGraphClassLive liveClass) {
-  final regExp = RegExp(knownClassesRegExs);
-  return regExp.allMatches(liveClass.name).isNotEmpty;
-}
-
-/// Returns a map of all datapoints collected:
-///
-///   key: 'externals'    value: List<ExternalReference>
-///   key: 'filters'      value: List<ClassReference>
-///   key: 'libraries'    value: List<ClassReference>
-///
-Map<String, List<Reference>> collect(
-  MemoryController controller,
-  Snapshot snapshot,
-) {
-  final Map<String, List<Reference>> result = {};
-
-  if (snapshot.libraryRoot == null) return {};
-
-  // Analyze the snapshot's heap memory information
-  final root = snapshot.libraryRoot!;
-  final heapGraph = controller.heapGraph;
-
-  for (final library in root.children) {
-    if (library.isExternals) {
-      final externalsToAnalyze = <ExternalReference>[];
-
-      final ExternalReferences externals = library as ExternalReferences;
-      for (final ExternalReference external
-          in externals.children.cast<ExternalReference>()) {
-        final liveExternal = external.liveExternal;
-        final size = liveExternal.externalProperty.externalSize;
-        final liveElement = liveExternal.live;
-        final HeapGraphClassLive liveClass =
-            liveElement.theClass as HeapGraphClassLive;
-        if (_classMatcher(liveClass)) {
-          final instances = liveClass.getInstances(heapGraph);
-          externalsToAnalyze.add(external);
-          debugLogger(
-            'Regex external found ${liveClass.name} '
-            'instances=${instances.length} '
-            'allocated bytes=$size',
-          );
-        }
-      }
-      result['externals'] = externalsToAnalyze;
-    } else if (library.isFiltered) {
-      final filtersToAnalyze = <ClassReference>[];
-      for (final Reference libraryRef in library.children) {
-        for (final ClassReference classRef
-            in libraryRef.children.cast<ClassReference>()) {
-          final HeapGraphClassLive liveClass = classRef.actualClass!;
-          if (_classMatcher(liveClass)) {
-            filtersToAnalyze.add(classRef);
-            final instances = liveClass.getInstances(heapGraph);
-            debugLogger(
-              'Regex filtered found ${classRef.name} '
-              'instances=${instances.length}',
-            );
-          }
-        }
-        result['filters'] = filtersToAnalyze;
-      }
-    } else if (library.isLibrary) {
-      final librariesToAnalyze = <ClassReference>[];
-      for (final ClassReference classRef
-          in library.children.cast<ClassReference>()) {
-        final HeapGraphClassLive liveClass = classRef.actualClass!;
-        if (_classMatcher(liveClass)) {
-          librariesToAnalyze.add(classRef);
-          final instances = liveClass.getInstances(heapGraph);
-          debugLogger(
-            'Regex library found ${classRef.name} '
-            'instances=${instances.length}',
-          );
-        }
-      }
-      result['libraries'] = librariesToAnalyze;
-    } else if (library.isAnalysis) {
-      // Nothing to do on anay analyses.
-    }
-  }
-
-  return result;
-}
-
-const bucket10K = '1..10K';
-const bucket50K = '10K..50K';
-const bucket100K = '50K..100K';
-const bucket500K = '100K..500K';
-const bucket1M = '500K..1M';
-const bucket10M = '1M..10M';
-const bucket50M = '10M..50M';
-const bucketBigM = '50M+';
-
-class Bucket {
-  Bucket(this.totalCount, this.totalBytes);
-
-  int totalCount;
-  int totalBytes;
-}
-
-void imageAnalysis(
-  MemoryController controller,
-  AnalysisSnapshotReference analysisSnapshot,
-  Map<String, List<Reference>> collectedData,
-) {
-  // TODO(terry): Look at heap rate of growth (used, external, RSS).
-
-  // TODO(terry): Any items with <empty> Reference.isEmpty need to be computed e.g., onExpand,
-  collectedData.forEach((key, value) {
-    switch (key) {
-      case 'externals':
-        final externalsNode = AnalysisReference('Externals');
-        analysisSnapshot.addChild(externalsNode);
-        for (final ExternalReference ref in value.cast<ExternalReference>()) {
-          final HeapGraphExternalLive liveExternal = ref.liveExternal;
-          final HeapGraphElementLive liveElement = liveExternal.live;
-
-          /// TODO(terry): Eliminate or show sentinels for total instances?
-          final objectNode = AnalysisReference(
-            '${ref.name}',
-            countNote: liveElement.theClass!.instancesCount,
-          );
-          externalsNode.addChild(objectNode);
-          var childExternalSizes = 0;
-          final bucketSizes = SplayTreeMap<String, Bucket>();
-          for (final ExternalObjectReference child
-              in ref.children.cast<ExternalObjectReference>()) {
-            if (child.externalSize < 10000) {
-              bucketSizes.putIfAbsent(bucket10K, () => Bucket(0, 0));
-              bucketSizes[bucket10K]!.totalCount += 1;
-              bucketSizes[bucket10K]!.totalBytes += child.externalSize;
-            } else if (child.externalSize < 50000) {
-              bucketSizes.putIfAbsent(bucket50K, () => Bucket(0, 0));
-              bucketSizes[bucket50K]!.totalCount += 1;
-              bucketSizes[bucket50K]!.totalBytes += child.externalSize;
-            } else if (child.externalSize < 100000) {
-              bucketSizes.putIfAbsent(bucket100K, () => Bucket(0, 0));
-              bucketSizes[bucket100K]!.totalCount += 1;
-              bucketSizes[bucket100K]!.totalBytes += child.externalSize;
-            } else if (child.externalSize < 500000) {
-              bucketSizes.putIfAbsent(bucket500K, () => Bucket(0, 0));
-              bucketSizes[bucket500K]!.totalCount += 1;
-              bucketSizes[bucket500K]!.totalBytes += child.externalSize;
-            } else if (child.externalSize < 1000000) {
-              bucketSizes.putIfAbsent(bucket1M, () => Bucket(0, 0));
-              bucketSizes[bucket1M]!.totalCount += 1;
-              bucketSizes[bucket1M]!.totalBytes += child.externalSize;
-            } else if (child.externalSize < 10000000) {
-              bucketSizes.putIfAbsent(bucket10M, () => Bucket(0, 0));
-              bucketSizes[bucket10M]!.totalCount += 1;
-              bucketSizes[bucket10M]!.totalBytes += child.externalSize;
-            } else if (child.externalSize < 50000000) {
-              bucketSizes.putIfAbsent(bucket50M, () => Bucket(0, 0));
-              bucketSizes[bucket50M]!.totalCount += 1;
-              bucketSizes[bucket50M]!.totalBytes += child.externalSize;
-            } else {
-              bucketSizes.putIfAbsent(bucketBigM, () => Bucket(0, 0));
-              bucketSizes[bucketBigM]!.totalCount += 1;
-              bucketSizes[bucketBigM]!.totalBytes += child.externalSize;
-            }
-
-            childExternalSizes += child.externalSize;
-          }
-
-          final bucketNode = AnalysisReference(
-            'Buckets',
-            sizeNote: childExternalSizes,
-          );
-          bucketSizes.forEach((key, value) {
-            bucketNode.addChild(
-              AnalysisReference(
-                '$key',
-                countNote: value.totalCount,
-                sizeNote: value.totalBytes,
-              ),
-            );
-          });
-          objectNode.addChild(bucketNode);
-        }
-        break;
-      case 'filters':
-      case 'libraries':
-        final librariesNode = AnalysisReference('Library $key');
-
-        final matches = drillIn(controller, librariesNode, value);
-
-        final imageCacheNode = processMatches(controller, matches);
-        if (imageCacheNode != null) {
-          librariesNode.addChild(imageCacheNode);
-        }
-
-        analysisSnapshot.addChild(librariesNode);
-    }
-  });
-}
-
-AnalysisReference? processMatches(
-  MemoryController controller,
-  Map<String, List<String>> matches,
-) {
-  // Root __FIELDS__ is a container for children, the children
-  // are added, later, to a treenode - if the treenode should
-  // be created.
-  final AnalysisField pending = AnalysisField(
-    '__FIELDS__',
-    null,
-  );
-  final AnalysisField cache = AnalysisField(
-    '__FIELDS__',
-    null,
-  );
-  final AnalysisField live = AnalysisField(
-    '__FIELDS__',
-    null,
-  );
-
-  var countPending = 0;
-  var countCache = 0;
-  var countLive = 0;
-  bool imageCacheFound = false;
-  matches.forEach((key, values) {
-    final fields = key.split('.');
-    imageCacheFound = fields[0] == imageCache;
-
-    for (final value in values) {
-      switch (fields[1]) {
-        case '_pendingImages':
-          countPending++;
-          pending.addChild(AnalysisField('url', value));
-          break;
-        case '_cache':
-          countCache++;
-          cache.addChild(AnalysisField('url', value));
-          break;
-        case '_liveImages':
-          countLive++;
-          live.addChild(AnalysisField('url', value));
-          break;
-      }
-    }
-  });
-
-  if (imageCacheFound) {
-    final imageCacheNode = AnalysisReference(
-      imageCache,
-      countNote: countPending + countCache + countLive,
-    );
-
-    final pendingNode = AnalysisReference(
-      'Pending',
-      countNote: countPending,
-    );
-
-    final cacheNode = AnalysisReference(
-      'Cache',
-      countNote: countCache,
-    );
-
-    final liveNode = AnalysisReference(
-      'Live',
-      countNote: countLive,
-    );
-
-    final pendingInstance = AnalysisInstance(
-      controller,
-      'Images',
-      pending,
-    );
-    final cacheInstance = AnalysisInstance(
-      controller,
-      'Images',
-      cache,
-    );
-    final liveInstance = AnalysisInstance(
-      controller,
-      'Images',
-      live,
-    );
-
-    pendingNode.addChild(pendingInstance);
-    imageCacheNode.addChild(pendingNode);
-
-    cacheNode.addChild(cacheInstance);
-    imageCacheNode.addChild(cacheNode);
-
-    liveNode.addChild(liveInstance);
-    imageCacheNode.addChild(liveNode);
-
-    return imageCacheNode;
-  }
-
-  return null;
-}
-
-// TODO(terry): Add a test, insure debugMonitor output never seen before checkin.
-
-/// Enable monitoring.
-bool _debugMonitorEnabled = false;
-
-// Name of classes to monitor then all field/object are followed with debug
-// information during drill in, e.g.,
-final _debugMonitorClasses = ['ImageCache'];
-
-/// Class being monitored if its name is in the debugMonitorClasses.
-String? _debugMonitorClass;
-
-void _debugMonitor(String msg) {
-  if (!_debugMonitorEnabled || _debugMonitorClass == null) return;
-  print('--> $_debugMonitorClass:$msg');
-}
-
-ClassFields fieldsStack = ClassFields();
-
-Map<String, List<String>> drillIn(
-  MemoryController? controller,
-  AnalysisReference librariesNode,
-  List<Reference> references, {
-  createTreeNodes = false,
-}) {
-  final Map<String, List<String>> result = {};
-
-  final matcher = ObjectMatcher((className, fields, value) {
-    final key = '$className.${fields.join(".")}';
-    result.putIfAbsent(key, () => []);
-    result[key]!.add(value);
-  });
-
-  for (final Reference classRef in references) {
-    if (classRef.name == null || !matcher.isClassMatched(classRef.name!)) {
-      continue;
-    }
-
-    final name = classRef.name!;
-
-    // Insure instances are realized (not Reference.empty).
-    computeInstanceForClassReference(controller, classRef);
-    final HeapGraphClassLive? liveClass = classRef.actualClass;
-
-    late AnalysisReference objectNode;
-    if (createTreeNodes) {
-      objectNode = AnalysisReference(
-        '$name',
-        countNote: liveClass!.instancesCount,
-      );
-      librariesNode.addChild(objectNode);
-    }
-
-    if (_debugMonitorEnabled) {
-      _debugMonitorClass = _debugMonitorClasses.contains(name) ? name : '';
-    }
-
-    fieldsStack.push(name);
-
-    var instanceIndex = 0;
-    _debugMonitor('Class $name Instance=$instanceIndex');
-    for (final ObjectReference objRef
-        in classRef.children.cast<ObjectReference>()) {
-      final fields = objRef.instance.getFields();
-      // Root __FIELDS__ is a container for children, the children
-      // are added, later, to a treenode - if the treenode should
-      // be created.
-      final AnalysisField fieldsRoot = AnalysisField(
-        '__FIELDS__',
-        null,
-      );
-
-      for (final field in fields) {
-        if (field.value.isSentinel) continue;
-
-        final HeapGraphElementLive live = field.value as HeapGraphElementLive;
-
-        if (live.references!.isNotEmpty) {
-          _debugMonitor('${field.key} OBJECT Start');
-
-          final fieldObjectNode = AnalysisField(field.key, '');
-
-          fieldsStack.push(field.key);
-          displayObject(
-            matcher,
-            fieldObjectNode,
-            live,
-            createTreeNodes: createTreeNodes,
-          );
-          fieldsStack.pop();
-
-          if (createTreeNodes) {
-            fieldsRoot.addChild(fieldObjectNode);
-          }
-          _debugMonitor('${field.key} OBJECT End');
-        } else {
-          final value = displayData(live);
-          if (value != null) {
-            fieldsStack.push(field.key);
-            matcher.findFieldMatch(fieldsStack, value);
-            fieldsStack.pop();
-
-            _debugMonitor('${field.key} = $value');
-            if (createTreeNodes) {
-              final fieldNode = AnalysisField(field.key, value);
-              fieldsRoot.addChild(fieldNode);
-            }
-          } else {
-            _debugMonitor('${field.key} Skipped null');
-          }
-        }
-      }
-
-      if (createTreeNodes) {
-        final instanceNode = AnalysisInstance(
-          controller,
-          'Instance $instanceIndex',
-          fieldsRoot,
-        );
-        objectNode.addChild(instanceNode);
-      }
-
-      instanceIndex++;
-    }
-
-    fieldsStack.pop(); // Pop class name.
-  }
-
-  return result;
-}
-
-bool? displayObject(
-  ObjectMatcher matcher,
-  AnalysisField objectField,
-  HeapGraphElementLive live, {
-  depth = 0,
-  maxDepth = 4,
-  createTreeNodes = false,
-}) {
-  if (depth >= maxDepth) return null;
-  if (live.references?.isEmpty ?? true) return true;
-
-  final fields = live.getFields();
-  for (final field in fields) {
-    if (field.value.isSentinel) continue;
-
-    final HeapGraphElementLive liveField = field.value as HeapGraphElementLive;
-    for (final ref in liveField.references ?? []) {
-      if (ref.isSentinel) continue;
-      final HeapGraphElementLive liveRef = ref as HeapGraphElementLive;
-      final objectFields = liveRef.getFields();
-      if (objectFields.isEmpty) continue;
-
-      final newObject = AnalysisField(field.key, '');
-      _debugMonitor('${field.key} OBJECT start [depth=$depth]');
-
-      depth++;
-
-      fieldsStack.push(field.key);
-      final continueResult = displayObject(
-        matcher,
-        newObject,
-        liveRef,
-        depth: depth,
-        createTreeNodes: createTreeNodes,
-      );
-      fieldsStack.pop();
-
-      depth--;
-      // Drilled in enough, stop.
-      if (continueResult == null) continue;
-
-      if (createTreeNodes) {
-        objectField.addChild(newObject);
-      }
-      _debugMonitor('${field.key} OBJECT end  [depth=$depth]');
-    }
-
-    final value = displayData(liveField);
-    if (value != null) {
-      fieldsStack.push(field.key);
-      matcher.findFieldMatch(fieldsStack, value);
-      fieldsStack.pop();
-
-      _debugMonitor('${field.key}=$value');
-      if (createTreeNodes) {
-        final node = AnalysisField(field.key, value);
-        objectField.addChild(node);
-      }
-    }
-  }
-
-  return true;
-}
-
-String? displayData(instance) {
-  String? result;
-
-  switch (instance.origin.data.runtimeType) {
-    case HeapSnapshotObjectNullData:
-    case HeapSnapshotObjectNoData:
-    case Null:
-    case TypeArguments:
-      break;
-    default:
-      result = '${instance.origin.data}';
-  }
-
-  return result;
-}
-
-class AnalysisInstanceViewTable extends StatefulWidget {
-  @override
-  AnalysisInstanceViewState createState() => AnalysisInstanceViewState();
-}
-
-/// Table of the fields of an instance (type, name and value).
-class AnalysisInstanceViewState extends State<AnalysisInstanceViewTable>
-    with
-        AutoDisposeMixin,
-        ProvidedControllerMixin<MemoryController, AnalysisInstanceViewTable> {
-  final TreeColumnData<AnalysisField> _treeColumn = _AnalysisFieldNameColumn();
-
-  final _columns = <ColumnData<AnalysisField>>[];
-
-  @override
-  void initState() {
-    super.initState();
-
-    // Setup the table columns.
-    _columns.addAll([
-      _treeColumn,
-      _AnalysisFieldValueColumn(),
-    ]);
-  }
-
-  @override
-  void didChangeDependencies() {
-    super.didChangeDependencies();
-
-    if (!initController()) return;
-    cancelListeners();
-
-    // Update the chart when the memorySource changes.
-    addAutoDisposeListener(controller.selectedSnapshotNotifier, () {
-      setState(() {
-        controller.computeAllLibraries(rebuild: true);
-      });
-    });
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    return TreeTable<AnalysisField>(
-      keyFactory: (typeRef) => PageStorageKey<String>(typeRef.name ?? ''),
-      dataRoots: controller.analysisInstanceRoot!,
-      dataKey: 'instance-fields',
-      columns: _columns,
-      treeColumn: _treeColumn,
-      defaultSortColumn: _columns[0],
-      defaultSortDirection: SortDirection.ascending,
-    );
-  }
-}
-
-class _AnalysisFieldNameColumn extends TreeColumnData<AnalysisField> {
-  _AnalysisFieldNameColumn() : super('Name');
-
-  @override
-  dynamic getValue(AnalysisField dataObject) => dataObject.name;
-
-  @override
-  String getDisplayValue(AnalysisField dataObject) => '${getValue(dataObject)}';
-
-  @override
-  bool get supportsSorting => true;
-
-  @override
-  int compare(AnalysisField a, AnalysisField b) {
-    final Comparable valueA = getValue(a);
-    final Comparable valueB = getValue(b);
-    return valueA.compareTo(valueB);
-  }
-
-  @override
-  double get fixedWidthPx => 250.0;
-}
-
-class _AnalysisFieldValueColumn extends ColumnData<AnalysisField> {
-  _AnalysisFieldValueColumn()
-      : super(
-          'Value',
-          fixedWidthPx: scaleByFontFactor(350.0),
-        );
-
-  @override
-  dynamic getValue(AnalysisField dataObject) => dataObject.value;
-
-  @override
-  String getDisplayValue(AnalysisField dataObject) {
-    var value = getValue(dataObject);
-    if (value is String && value.length > 30) {
-      value = '${value.substring(0, 13)}…${value.substring(value.length - 17)}';
-    }
-    return '$value';
-  }
-
-  @override
-  bool get supportsSorting => true;
-
-  @override
-  int compare(AnalysisField a, AnalysisField b) {
-    final Comparable valueA = getValue(a);
-    final Comparable valueB = getValue(b);
-    return valueA.compareTo(valueB);
-  }
-}
-
-class ClassFields {
-  final List<String> _fields = [];
-
-  void clear() {
-    _fields.clear();
-  }
-
-  int get length => _fields.length;
-
-  void push(String name) {
-    _fields.add(name);
-  }
-
-  String pop() => _fields.removeLast();
-
-  String elementAt(int index) => _fields.elementAt(index);
-}
-
-const imageCache = 'ImageCache';
-
-/// Callback function when an ObjectReference's class name and fields all match.
-/// Parameters:
-///   className that matched
-///   fields that all matched
-///   value of the matched objectReference
-///
-typedef CompletedFunction = void Function(
-  String className,
-  List<String> fields,
-  dynamic value,
-);
-
-class ObjectMatcher {
-  ObjectMatcher(this._matchCompleted);
-
-  static const Map<String, List<List<String>>> matcherDrillIn = {
-    '$imageCache': [
-      ['_pendingImages', 'data_', 'completer', 'context_', 'url'],
-      ['_cache', 'data_', 'completer', 'context_', 'url'],
-      ['_liveImages', 'data_', 'completer', 'context_', 'url'],
-    ]
-  };
-
-  final CompletedFunction _matchCompleted;
-
-  bool isClassMatched(String className) =>
-      matcherDrillIn.containsKey(className);
-
-  List<List<String>>? _findClassMatch(String className) =>
-      matcherDrillIn[className];
-
-  // TODO(terry): Change to be less strict.  Look for subclass or parentage
-  //              relationships.  If a new field or subclass is added we can
-  //              still find what we're looking for.  Maybe even consider the
-  //              the type we're looking for - best to be loosey goosey so
-  //              we're not brittle as the Framework or any code changes.
-  /// First field name match.
-  bool findFieldMatch(ClassFields classFields, dynamic value) {
-    bool matched = false;
-
-    final className = classFields._fields.elementAt(0);
-    final listOfFieldsToMatch = _findClassMatch(className);
-
-    if (listOfFieldsToMatch != null) {
-      for (final fieldsToMatch in listOfFieldsToMatch) {
-        final fieldsSize = fieldsToMatch.length;
-        if (fieldsSize == classFields._fields.length - 1) {
-          for (var index = 0; index < fieldsSize; index++) {
-            if (fieldsToMatch[index] ==
-                classFields._fields.elementAt(index + 1)) {
-              matched = true;
-            } else {
-              matched = false;
-              break;
-            }
-          }
-        }
-
-        if (matched) {
-          _matchCompleted(className, fieldsToMatch, value);
-          break;
-        }
-      }
-    }
-
-    return matched;
-  }
-}
diff --git a/packages/devtools_app/lib/src/screens/memory/memory_controller.dart b/packages/devtools_app/lib/src/screens/memory/memory_controller.dart
index 7b4543b..9c3c9c7 100644
--- a/packages/devtools_app/lib/src/screens/memory/memory_controller.dart
+++ b/packages/devtools_app/lib/src/screens/memory/memory_controller.dart
@@ -4,7 +4,6 @@
 
 import 'dart:async';
 
-import 'package:collection/collection.dart' show IterableExtension;
 import 'package:devtools_shared/devtools_shared.dart';
 import 'package:flutter/foundation.dart';
 import 'package:intl/intl.dart';
@@ -19,15 +18,10 @@
 import '../../service/service_extensions.dart';
 import '../../service/service_manager.dart';
 import '../../shared/globals.dart';
-import '../../shared/table/table.dart';
 import '../../shared/utils.dart';
-import '../../ui/search.dart';
-import 'memory_graph_model.dart';
 import 'memory_protocol.dart';
-import 'memory_snapshot_models.dart';
 import 'panes/allocation_profile/allocation_profile_table_view_controller.dart';
 import 'panes/diff/controller/diff_pane_controller.dart';
-import 'primitives/filter_config.dart';
 import 'primitives/memory_timeline.dart';
 import 'shared/heap/model.dart';
 
@@ -115,13 +109,10 @@
 /// This class must not have direct dependencies on dart:html. This allows tests
 /// of the complicated logic in this class to run on the VM.
 class MemoryController extends DisposableController
-    with
-        AutoDisposeControllerMixin,
-        SearchControllerMixin,
-        AutoCompleteSearchControllerMixin {
+    with AutoDisposeControllerMixin {
   MemoryController({DiffPaneController? diffPaneController}) {
     memoryTimeline = MemoryTimeline(offline);
-    memoryLog = MemoryLog(this);
+    memoryLog = _MemoryLog(this);
     this.diffPaneController =
         diffPaneController ?? DiffPaneController(SnapshotTaker());
 
@@ -147,148 +138,14 @@
 
   static const logFilenamePrefix = 'memory_log_';
 
-  final List<Snapshot> snapshots = [];
-
-  Snapshot? get lastSnapshot => snapshots.safeLast;
-
   /// Root nodes names that contains nodes of either libraries or classes depending on
   /// group by library or group by class.
   static const libraryRootNode = '___LIBRARY___';
   static const classRootNode = '___CLASSES___';
 
-  /// Notifies that the source of the memory feed has changed.
-  ValueListenable<DateTime?> get selectedSnapshotNotifier =>
-      _selectedSnapshotNotifier;
-
   final _shouldShowLeaksTab = ValueNotifier<bool>(false);
   ValueListenable<bool> get shouldShowLeaksTab => _shouldShowLeaksTab;
 
-  static String formattedTimestamp(DateTime? timestamp) =>
-      timestamp != null ? DateFormat('MMM dd HH:mm:ss').format(timestamp) : '';
-
-  /// Stored value is pretty timestamp when the snapshot was done.
-  final _selectedSnapshotNotifier = ValueNotifier<DateTime?>(null);
-
-  set selectedSnapshotTimestamp(DateTime? snapshotTimestamp) {
-    _selectedSnapshotNotifier.value = snapshotTimestamp;
-  }
-
-  DateTime? get selectedSnapshotTimestamp => _selectedSnapshotNotifier.value;
-
-  HeapGraph? heapGraph;
-
-  /// Leaf node of tabletree snapshot selected?  If selected then the instance
-  /// view is displayed to view the fields of an instance.
-  final _leafSelectedNotifier = ValueNotifier<HeapGraphElementLive?>(null);
-
-  ValueListenable<HeapGraphElementLive?> get leafSelectedNotifier =>
-      _leafSelectedNotifier;
-
-  HeapGraphElementLive? get selectedLeaf => _leafSelectedNotifier.value;
-
-  set selectedLeaf(HeapGraphElementLive? selected) {
-    _leafSelectedNotifier.value = selected;
-  }
-
-  bool get isLeafSelected => selectedLeaf != null;
-
-  void computeRoot() {
-    if (selectedLeaf != null) {
-      final root = instanceToFieldNodes(this, selectedLeaf!);
-      _instanceRoot = root.isNotEmpty ? root : [FieldReference.empty];
-    } else {
-      _instanceRoot = [FieldReference.empty];
-    }
-  }
-
-  List<FieldReference>? _instanceRoot;
-
-  List<FieldReference>? get instanceRoot => _instanceRoot;
-
-  /// Leaf node of analysis selected?  If selected then the field
-  /// view is displayed to view an abbreviated fields of an instance.
-  final _leafAnalysisSelectedNotifier = ValueNotifier<AnalysisInstance?>(null);
-
-  ValueListenable<AnalysisInstance?> get leafAnalysisSelectedNotifier =>
-      _leafAnalysisSelectedNotifier;
-
-  AnalysisInstance? get selectedAnalysisLeaf =>
-      _leafAnalysisSelectedNotifier.value;
-
-  set selectedAnalysisLeaf(AnalysisInstance? selected) {
-    _leafAnalysisSelectedNotifier.value = selected;
-  }
-
-  bool get isAnalysisLeafSelected => selectedAnalysisLeaf != null;
-
-  void computeAnalysisInstanceRoot() {
-    if (selectedAnalysisLeaf != null) {
-      final List<AnalysisField> analysisFields =
-          selectedAnalysisLeaf!.fieldsRoot.children;
-      _analysisInstanceRoot =
-          analysisFields.isNotEmpty ? analysisFields : [AnalysisField.empty];
-    } else {
-      _analysisInstanceRoot = [AnalysisField.empty];
-    }
-  }
-
-  List<AnalysisField>? _analysisInstanceRoot;
-
-  List<AnalysisField>? get analysisInstanceRoot => _analysisInstanceRoot;
-
-  // List of completed Analysis of Snapshots.
-  final List<AnalysisSnapshotReference> completedAnalyses = [];
-
-  /// Determine the snapshot to analyze - current active snapshot (selected or node
-  /// under snapshot selected), last snapshot or null (unknown).
-  Snapshot? get computeSnapshotToAnalyze {
-    // Any snapshots to analyze?
-    if (snapshots.isEmpty) return null;
-
-    // Is a selected table row under a snapshot.
-    final nodeSelected = selectionSnapshotNotifier.value.node;
-    final snapshot = getSnapshot(nodeSelected);
-    if (snapshot != null) {
-      // Has the snapshot (with a selected row) been analyzed?
-      return _findSnapshotAnalyzed(snapshot);
-    }
-
-    final snapshotsCount = snapshots.length;
-    final analysesCount = completedAnalyses.length;
-
-    // Exactly one analysis is left? Ff the 'Analysis' button is pressed the
-    // snapshot that is left will be processed (usually the last one). More
-    // than one snapshots to analyze, the user must select the snapshot to
-    // analyze.
-    if (snapshotsCount > analysesCount &&
-        snapshotsCount == (analysesCount + 1)) {
-      // Has the last snapshot been analyzed?
-      return _findSnapshotAnalyzed(lastSnapshot!);
-    }
-
-    return null;
-  }
-
-  /// Has the snapshot been analyzed, if not return the snapshot otherwise null.
-  Snapshot? _findSnapshotAnalyzed(Snapshot snapshot) {
-    final snapshotDateTime = snapshot.collectedTimestamp;
-    final foundMatch = completedAnalyses
-        .where((analysis) => analysis.dateTime == snapshotDateTime);
-    if (foundMatch.isEmpty) return snapshot;
-
-    return null;
-  }
-
-  ValueListenable get treeMapVisible => _treeMapVisible;
-
-  final _treeMapVisible = ValueNotifier<bool>(false);
-
-  void toggleTreeMapVisible(bool value) {
-    _treeMapVisible.value = value;
-  }
-
-  bool isAnalyzeButtonEnabled() => computeSnapshotToAnalyze != null;
-
   ValueListenable get legendVisibleNotifier => _legendVisibleNotifier;
 
   final _legendVisibleNotifier = ValueNotifier<bool>(false);
@@ -300,7 +157,7 @@
 
   late MemoryTimeline memoryTimeline;
 
-  late MemoryLog memoryLog;
+  late _MemoryLog memoryLog;
 
   /// Source of memory heap samples. False live data, True loaded from a
   /// memory_log file.
@@ -434,17 +291,6 @@
 
   final isAndroidChartVisibleNotifier = ValueNotifier<bool>(false);
 
-  final settings = SettingsModel();
-
-  final selectionSnapshotNotifier =
-      ValueNotifier<Selection<Reference?>>(Selection.empty());
-
-  /// Tree to view Libary/Class/Instance (grouped by)
-  late TreeTable<Reference> groupByTreeTable;
-
-  /// Tree to view fields of an instance.
-  TreeTable<FieldReference>? instanceFieldsTreeTable;
-
   final _updateClassStackTraces = ValueNotifier(0);
 
   ValueListenable<int> get updateClassStackTraces => _updateClassStackTraces;
@@ -453,104 +299,6 @@
     _updateClassStackTraces.value += 1;
   }
 
-  final FilterConfig filterConfig = FilterConfig(
-    filterZeroInstances: ValueNotifier(true),
-    filterLibraryNoInstances: ValueNotifier(true),
-    filterPrivateClasses: ValueNotifier(true),
-    libraryFilters: FilteredLibraries(),
-  );
-
-  /// All known libraries of the selected snapshot.
-  LibraryReference? get libraryRoot {
-    // Find the selected snapshot's libraryRoot.
-    final snapshot = getSnapshot(selectionSnapshotNotifier.value.node);
-    if (snapshot != null) return snapshot.libraryRoot;
-
-    return null;
-  }
-
-  /// Re-compute the libraries (possible filter change).
-  set libraryRoot(LibraryReference? newRoot) {
-    Snapshot? snapshot;
-
-    // Use last snapshot.
-    if (snapshots.isNotEmpty) {
-      snapshot = snapshots.safeLast;
-    }
-
-    // Find the selected snapshot's libraryRoot.
-    snapshot ??= getSnapshot(selectionSnapshotNotifier.value.node);
-    snapshot?.libraryRoot = newRoot;
-  }
-
-  /// Using the tree table find the active snapshot (selected or last snapshot).
-  SnapshotReference get activeSnapshot {
-    for (final topLevel in groupByTreeTable.dataRoots) {
-      if (topLevel is SnapshotReference) {
-        final nodeSelected = selectionSnapshotNotifier.value.node;
-        final snapshot = getSnapshot(nodeSelected);
-        final SnapshotReference snapshotRef = topLevel;
-        if (snapshot != null &&
-            snapshotRef.snapshot.collectedTimestamp ==
-                snapshot.collectedTimestamp) {
-          return topLevel;
-        }
-      }
-    }
-
-    // No selected snapshot so return the last snapshot.
-    final lastSnapshot = groupByTreeTable.dataRoots.safeLast!;
-    assert(lastSnapshot is SnapshotReference);
-
-    return lastSnapshot as SnapshotReference;
-  }
-
-  /// Given a node return its snapshot.
-  Snapshot? getSnapshot(Reference? reference) {
-    while (reference != null) {
-      if (reference is SnapshotReference) {
-        final SnapshotReference snapshotRef = reference;
-        return snapshotRef.snapshot;
-      }
-      reference = reference.parent;
-    }
-
-    return null;
-  }
-
-  /// Root node of all known analysis and snapshots.
-  LibraryReference? topNode;
-
-  /// Root of known classes (used for group by class).
-  LibraryReference? classRoot;
-
-  /// Notify that the filtering has changed.
-  ValueListenable<int> get filterNotifier => _filterNotifier;
-
-  final _filterNotifier = ValueNotifier<int>(0);
-
-  void updateFilter() {
-    _filterNotifier.value++;
-  }
-
-  ValueListenable<bool> get filterZeroInstancesListenable =>
-      filterConfig.filterZeroInstances;
-
-  ValueListenable<bool> get filterPrivateClassesListenable =>
-      filterConfig.filterPrivateClasses;
-
-  ValueListenable<bool> get filterLibraryNoInstancesListenable =>
-      filterConfig.filterLibraryNoInstances;
-
-  /// Table ordered by library, class or instance
-  static const groupByLibrary = 'Library';
-  static const groupByClass = 'Class';
-  static const groupByInstance = 'Instance';
-
-  final groupingBy = ValueNotifier<String>(groupByLibrary);
-
-  ValueListenable<String> get groupingByNotifier => groupingBy;
-
   String? get _isolateId =>
       serviceManager.isolateManager.selectedIsolate.value?.id;
 
@@ -712,224 +460,6 @@
   //              to line number of the source file when clicked is needed.
   static const packageName = '/packages/';
 
-  /// When new snapshot occurs entire libraries should be rebuilt then rebuild should be true.
-  LibraryReference? computeAllLibraries({
-    bool filtered = true,
-    bool rebuild = false,
-    HeapSnapshotGraph? graph,
-  }) {
-    final HeapSnapshotGraph? snapshotGraph =
-        graph != null ? graph : snapshots.safeLast?.snapshotGraph;
-
-    if (snapshotGraph == null) return null;
-
-    if (filtered && libraryRoot != null && !rebuild) return libraryRoot;
-
-    // Group by library
-    final newLibraryRoot = LibraryReference(this, libraryRootNode, null);
-
-    // Group by class (under root library __CLASSES__).
-    classRoot = LibraryReference(this, classRootNode, null);
-
-    final externalReferences =
-        ExternalReferences(this, snapshotGraph.externalSize);
-    for (final liveExternal
-        in heapGraph?.externals ?? <HeapGraphExternalLive>[]) {
-      if (liveExternal == null) continue;
-
-      final HeapGraphClassLive? classLive =
-          liveExternal.live.theClass as HeapGraphClassLive?;
-
-      ExternalReference? externalReference;
-
-      if (externalReferences.children.isNotEmpty) {
-        externalReference = externalReferences.children.singleWhereOrNull(
-          (knownClass) => knownClass.name == classLive?.name,
-        ) as ExternalReference?;
-      }
-
-      if (externalReference == null) {
-        externalReference =
-            ExternalReference(this, classLive?.name ?? '', liveExternal);
-        externalReferences.addChild(externalReference);
-      }
-
-      final classInstance = ExternalObjectReference(
-        this,
-        externalReference.children.length,
-        liveExternal.live,
-        liveExternal.externalProperty.externalSize,
-      );
-
-      // Sum up the externalSize of the children, under the externalReference group.
-      externalReference.sumExternalSizes +=
-          liveExternal.externalProperty.externalSize;
-
-      externalReference.addChild(classInstance);
-    }
-
-    newLibraryRoot.addChild(externalReferences);
-
-    // Add our filtered items under the 'Filtered' node.
-    if (filtered) {
-      final filteredReference = FilteredReference(this);
-      final filtered = heapGraph!.filteredLibraries;
-      addAllToNode(filteredReference, filtered);
-
-      newLibraryRoot.addChild(filteredReference);
-    }
-
-    // Compute all libraries.
-    final groupBy =
-        filtered ? heapGraph!.groupByLibrary : heapGraph!.rawGroupByLibrary;
-
-    groupBy.forEach((libraryName, classes) {
-      LibraryReference? libReference =
-          newLibraryRoot.children.singleWhereOrNull((library) {
-        return libraryName == library.name;
-      }) as LibraryReference?;
-
-      // Library not found add to list of children.
-      if (libReference == null) {
-        libReference = LibraryReference(this, libraryName, classes);
-        newLibraryRoot.addChild(libReference);
-      }
-
-      for (var actualClass in libReference.actualClasses ?? {}) {
-        monitorClass(
-          className: actualClass!.name,
-          message: 'computeAllLibraries',
-        );
-        final classRef = ClassReference(this, actualClass);
-        classRef.addChild(Reference.empty);
-
-        libReference.addChild(classRef);
-
-        // TODO(terry): Consider adding the ability to clear the table tree cache
-        // (root) to reset the level/depth values.
-        final classRefClassGroupBy = ClassReference(this, actualClass);
-        classRefClassGroupBy.addChild(Reference.empty);
-        classRoot!.addChild(classRefClassGroupBy);
-      }
-    });
-
-    // TODO(terry): Eliminate chicken and egg issue.
-    // This may not be set if snapshot is being computed, first-time.  Returning
-    // newLibraryRoot allows new snapshot to store the libraryRoot.
-    libraryRoot = newLibraryRoot;
-
-    return newLibraryRoot;
-  }
-
-  // TODO(terry): Change to Set of known libraries so it's O(n) instead of O(n^2).
-  void addAllToNode(
-    Reference root,
-    Map<String, Set<HeapGraphClassLive>> allItems,
-  ) {
-    allItems.forEach((libraryName, classes) {
-      LibraryReference? libReference =
-          root.children.singleWhereOrNull((library) {
-        return libraryName == library.name;
-      }) as LibraryReference?;
-
-      // Library not found add to list of children.
-      libReference ??= LibraryReference(this, libraryName, classes);
-      root.addChild(libReference);
-
-      for (var actualClass in libReference.actualClasses ?? {}) {
-        monitorClass(
-          className: actualClass!.name,
-          message: 'computeAllLibraries',
-        );
-        final classRef = ClassReference(this, actualClass);
-        classRef.addChild(Reference.empty);
-
-        libReference.addChild(classRef);
-
-        // TODO(terry): Consider adding the ability to clear the table tree cache
-        // (root) to reset the level/depth values.
-        final classRefClassGroupBy = ClassReference(this, actualClass);
-        classRefClassGroupBy.addChild(Reference.empty);
-        classRoot!.addChild(classRefClassGroupBy);
-      }
-    });
-  }
-
-  AnalysesReference? findAnalysesNode() {
-    if (topNode == null) return null;
-
-    for (final child in topNode!.children) {
-      if (child is AnalysesReference) {
-        return child;
-      }
-    }
-    return null;
-  }
-
-  void createSnapshotEntries(Reference? parent) {
-    for (final snapshot in snapshots) {
-      final Reference? snaphotMatch =
-          parent!.children.firstWhereOrNull((element) {
-        var result = false;
-        if (element is SnapshotReference) {
-          final SnapshotReference node = element;
-          result = node.snapshot == snapshot;
-        }
-
-        return result;
-      });
-      if (snaphotMatch == null) {
-        // New snapshot add it.
-        final snapshotNode = SnapshotReference(snapshot);
-        parent.addChild(snapshotNode);
-
-        final allLibraries =
-            computeAllLibraries(graph: snapshot.snapshotGraph)!;
-        snapshotNode.addAllChildren(allLibraries.children);
-
-        return;
-      }
-    }
-  }
-
-  final _treeChangedNotifier = ValueNotifier<bool>(false);
-
-  ValueListenable<bool> get treeChangedNotifier => _treeChangedNotifier;
-
-  bool get isTreeChanged => _treeChangedNotifier.value;
-
-  void treeChanged({bool state = true}) {
-    if (_treeChangedNotifier.value) {
-      _treeChangedNotifier.value = false;
-    }
-    _treeChangedNotifier.value = state;
-  }
-
-  Reference? buildTreeFromAllData() {
-    final List<Reference>? oldChildren = topNode?.children;
-    if (isTreeChanged) topNode = null;
-    topNode ??= LibraryReference(this, libraryRootNode, null);
-
-    if (isTreeChanged && oldChildren != null) {
-      topNode!.addAllChildren(oldChildren);
-    }
-
-    final anyAnalyses = topNode!.children
-            .firstWhereOrNull((reference) => reference is AnalysesReference) !=
-        null;
-
-    if (snapshots.isNotEmpty && !anyAnalyses) {
-      // Create Analysis entry.
-      final analysesRoot = AnalysesReference();
-      analysesRoot.addChild(AnalysisReference(''));
-      topNode!.addChild(analysesRoot);
-    }
-
-    createSnapshotEntries(topNode);
-
-    return topNode;
-  }
-
   Future getObject(String objectRef) async =>
       await serviceManager.service!.getObject(
         _isolateId!,
@@ -952,35 +482,6 @@
     }
   }
 
-  List<Reference>? snapshotByLibraryData;
-
-  void createSnapshotByLibrary() {
-    snapshotByLibraryData ??= lastSnapshot?.librariesToList();
-  }
-
-  Snapshot storeSnapshot(
-    DateTime timestamp,
-    HeapSnapshotGraph graph,
-    LibraryReference libraryRoot, {
-    bool autoSnapshot = false,
-  }) {
-    final snapshot = Snapshot(
-      timestamp,
-      this,
-      graph,
-      libraryRoot,
-      autoSnapshot,
-    );
-    snapshots.add(snapshot);
-
-    return snapshot;
-  }
-
-  void clearAllSnapshots() {
-    snapshots.clear();
-    snapshotByLibraryData = null;
-  }
-
   /// Detect stale isolates (sentinaled), may happen after a hot restart.
   Future<bool> isIsolateLive(String isolateId) async {
     try {
@@ -1009,23 +510,6 @@
   }
 }
 
-/// Settings dialog model.
-class SettingsModel {
-  /// Pattern is of the form:
-  ///    - empty string implies no matching.
-  ///    - NNN* implies match anything starting with NNN.
-  ///    - *NNN implies match anything ending with NNN.
-  String pattern = '';
-
-  /// If true hide Class names that begin with an underscore.
-  bool hidePrivateClasses = true;
-
-  /// If true enable the memory experiment that following a object instance via
-  /// inbound references instances.  Compares hashCodes (using eval causing
-  /// memory shaking).  Only works in debug mode.
-  bool experiment = false;
-}
-
 /// Index in datasets to each dataset's list of Entry's.
 enum ChartDataSets {
   // Datapoint entries for each used heap value.
@@ -1040,29 +524,9 @@
   rasterPictureSet,
 }
 
-/// Index in datasets to each dataset's list of Entry's.
-enum EventDataSets {
-  // Datapoint entries for ghost trace to stop auto-scaling of Y-axis.
-  ghostsSet,
-  // Datapoint entries for each user initiated GC.
-  gcUserSet,
-  // Datapoint entries for a VM's GC.
-  gcVmSet,
-  // Datapoint entries for each user initiated snapshot event.
-  snapshotSet,
-  // Datapoint entries for an automatically initiated snapshot event.
-  snapshotAutoSet,
-  // Allocation Accumulator monitoring.
-  monitorStartSet,
-  // TODO(terry): Allocation Accumulator continues UX connector.
-  monitorContinuesSet,
-  // Reset all Allocation Accumulators.
-  monitorResetSet,
-}
-
 /// Supports saving and loading memory samples.
-class MemoryLog {
-  MemoryLog(this.controller);
+class _MemoryLog {
+  _MemoryLog(this.controller);
 
   /// Use in memory or local file system based on Flutter Web/Desktop.
   static final _fs = FileIO();
@@ -1128,6 +592,7 @@
   }
 
   /// Load the memory profile data from a saved memory log file.
+  @visibleForTesting
   Future<void> loadOffline(String filename) async {
     final jsonPayload = _fs.readStringFromFile(filename, isMemory: true)!;
 
diff --git a/packages/devtools_app/lib/src/screens/memory/memory_filter.dart b/packages/devtools_app/lib/src/screens/memory/memory_filter.dart
deleted file mode 100644
index 4962cd8..0000000
--- a/packages/devtools_app/lib/src/screens/memory/memory_filter.dart
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:flutter/material.dart';
-
-import '../../primitives/auto_dispose_mixin.dart';
-import '../../shared/common_widgets.dart';
-import '../../shared/dialogs.dart';
-import '../../shared/theme.dart';
-import 'memory_controller.dart';
-import 'memory_snapshot_models.dart';
-import 'primitives/filter_config.dart';
-
-/// Name displayed in filter dialog, for wildcard groups.
-const _prettyPrintDartAbbreviation = '$dartLibraryUriPrefix*';
-const _prettyPrintFlutterAbbreviation = '$flutterLibraryUriPrefix*';
-
-/// State of the libraries and packages (hidden or not) for the filter dialog.
-class LibraryFilter {
-  LibraryFilter(this.displayName, this.hide);
-
-  /// Displayed library name.
-  final String displayName;
-
-  /// Whether classes in this library hidden (filtered).
-  bool hide = false;
-}
-
-class SnapshotFilterDialog extends StatefulWidget {
-  const SnapshotFilterDialog(this.controller);
-
-  final MemoryController controller;
-
-  @override
-  SnapshotFilterState createState() => SnapshotFilterState();
-}
-
-class SnapshotFilterState extends State<SnapshotFilterDialog>
-    with AutoDisposeMixin {
-  bool _intitialized = false;
-
-  late final bool oldFilterPrivateClassesValue;
-
-  late final bool oldFilterZeroInstancesValue;
-
-  late final bool oldFilterLibraryNoInstancesValue;
-
-  final oldFilteredLibraries = <String, bool>{};
-
-  @override
-  void didChangeDependencies() {
-    super.didChangeDependencies();
-
-    if (_intitialized) return;
-    _intitialized = true;
-
-    buildFilters();
-
-    final parameters = widget.controller.filterConfig;
-    oldFilterPrivateClassesValue = parameters.filterPrivateClasses.value;
-    oldFilterZeroInstancesValue = parameters.filterZeroInstances.value;
-    oldFilterLibraryNoInstancesValue =
-        parameters.filterLibraryNoInstances.value;
-  }
-
-  void addLibrary(String libraryName, {bool hideState = false}) {
-    final filteredGroup = <String, List<LibraryFilter>>{};
-    final filters = widget.controller.filterConfig.libraryFilters;
-
-    final isFiltered = filters.isLibraryFiltered(libraryName);
-    var groupedName = libraryName;
-    bool hide = hideState;
-    if (isFiltered) {
-      if (filters.isDartLibraryName(libraryName)) {
-        groupedName = _prettyPrintDartAbbreviation;
-      } else if (filters.isFlutterLibraryName(libraryName)) {
-        groupedName = _prettyPrintFlutterAbbreviation;
-      }
-    }
-    hide = isFiltered;
-
-    // Used by checkboxes in dialog.
-    filteredGroup[groupedName] ??= [];
-    filteredGroup[groupedName]!.add(LibraryFilter(libraryName, hide));
-  }
-
-  void buildFilters() {
-    // First time filters created, populate with the default list of libraries
-    // to filters
-
-    for (final library
-        in widget.controller.filterConfig.libraryFilters.librariesFiltered) {
-      addLibrary(library, hideState: true);
-    }
-    // If not snapshots, return no libraries to process.
-    if (widget.controller.snapshots.isEmpty) return;
-
-    // No libraries to compute until a snapshot exist.
-    if (widget.controller.snapshots.isEmpty) return;
-
-    final List<Reference> libraries = widget.controller.libraryRoot == null
-        ? widget.controller.activeSnapshot.children
-        : widget.controller.libraryRoot!.children;
-
-    libraries..sort((a, b) => a.name!.compareTo(b.name!));
-
-    for (final library in libraries) {
-      // Don't include external and filtered these are a composite and can't be filtered out.
-      if (library.name != externalLibraryName &&
-          library.name != filteredLibrariesName) {
-        assert(library.name != null);
-        addLibrary(library.name!);
-      }
-    }
-  }
-
-  Widget applyAndCancelButton() {
-    return Row(
-      mainAxisAlignment: MainAxisAlignment.end,
-      children: [
-        DialogApplyButton(
-          onPressed: () {
-            // Re-generate librariesFiltered
-            widget.controller.filterConfig.libraryFilters.clearFilters();
-            // Re-filter the groups.
-            widget.controller.libraryRoot = null;
-            if (widget.controller.lastSnapshot != null) {
-              widget.controller.heapGraph!.computeFilteredGroups();
-              widget.controller.computeAllLibraries(
-                graph: widget.controller.lastSnapshot!.snapshotGraph,
-              );
-            }
-
-            widget.controller.updateFilter();
-          },
-        ),
-        const SizedBox(width: defaultSpacing),
-        DialogCancelButton(
-          cancelAction: () {
-            widget.controller.filterConfig.filterPrivateClasses.value =
-                oldFilterPrivateClassesValue;
-            widget.controller.filterConfig.filterZeroInstances.value =
-                oldFilterZeroInstancesValue;
-            widget.controller.filterConfig.filterLibraryNoInstances.value =
-                oldFilterLibraryNoInstancesValue;
-          },
-        ),
-      ],
-    );
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    // Dialog has three main vertical sections:
-    //      - three checkboxes
-    //      - one list of libraries with at least 5 entries
-    //      - one row of buttons Ok/Cancel
-    // For very tall app keep the dialog at a reasonable height w/o too much vertical whitespace.
-    // The listbox area is the area that grows to accommodate the list of known libraries.
-
-    final theme = Theme.of(context);
-
-    return DevToolsDialog(
-      title: const DialogTitleText('Memory Filter Libraries and Classes'),
-      includeDivider: false,
-      content: Container(
-        width: defaultDialogWidth,
-        child: Padding(
-          padding: const EdgeInsets.only(left: 15),
-          child: Column(
-            crossAxisAlignment: CrossAxisAlignment.start,
-            children: [
-              Column(
-                crossAxisAlignment: CrossAxisAlignment.start,
-                children: [
-                  ...dialogSubHeader(theme, 'Snapshot Filters'),
-                  Row(
-                    children: [
-                      NotifierCheckbox(
-                        notifier:
-                            widget.controller.filterConfig.filterPrivateClasses,
-                      ),
-                      const DevToolsTooltip(
-                        message: 'Hide class names beginning with '
-                            'an underscore e.g., _className',
-                        child: Text('Hide Private Classes'),
-                      ),
-                    ],
-                  ),
-                  Row(
-                    children: [
-                      NotifierCheckbox(
-                        notifier:
-                            widget.controller.filterConfig.filterZeroInstances,
-                      ),
-                      const Text('Hide Classes with No Instances'),
-                    ],
-                  ),
-                  Row(
-                    children: [
-                      NotifierCheckbox(
-                        notifier: widget
-                            .controller.filterConfig.filterLibraryNoInstances,
-                      ),
-                      const Text('Hide Library with No Instances'),
-                    ],
-                  ),
-                  applyAndCancelButton(),
-                ],
-              ),
-            ],
-          ),
-        ),
-      ),
-    );
-  }
-}
diff --git a/packages/devtools_app/lib/src/screens/memory/memory_graph_model.dart b/packages/devtools_app/lib/src/screens/memory/memory_graph_model.dart
deleted file mode 100644
index 9ffd11f..0000000
--- a/packages/devtools_app/lib/src/screens/memory/memory_graph_model.dart
+++ /dev/null
@@ -1,482 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:vm_service/vm_service.dart';
-
-// import '../config_specific/logger/logger.dart';
-import '../../primitives/utils.dart';
-import 'primitives/filter_config.dart';
-import 'primitives/predefined_classes.dart';
-
-// TODO(terry): Investigate if class implements the Map interface?
-bool isBuiltInMap(HeapGraphClassLive live) =>
-    live.fullQualifiedName == predefinedMap ||
-    live.fullQualifiedName == predefinedHashMap;
-
-/// Is it a built-in HashMap class (predefined).
-bool isBuiltInHashMap(HeapGraphClassLive live) =>
-    live.fullQualifiedName == predefinedHashMap;
-
-/// Is it a built-in List class (predefined).
-bool isBuiltInList(HeapGraphClassLive live) =>
-    live.fullQualifiedName == predefinedList;
-
-/// List of classes to monitor, helps to debug particular class structure.
-final Map<int, String> _monitorClasses = {};
-
-/// Ensure the classId is zero based ()
-bool monitorClass({int? classId, String? className, String message = ''}) {
-  if (classId != null) {
-    if (_monitorClasses.containsKey(classId)) {
-      final className = _monitorClasses[classId];
-      print('STOP: class $className [classId=$classId]  $message');
-      return true;
-    }
-  } else if (className != null) {
-    if (_monitorClasses.containsValue(className)) {
-      print('STOP: class $className  $message');
-      return true;
-    }
-  } else {
-    print('WARNING: Missing classId or className to monitor.');
-  }
-  return false;
-}
-
-HeapGraph convertHeapGraph(
-  FilterConfig filterConfig,
-  HeapSnapshotGraph graph, [
-  List<String>? classNamesToMonitor,
-]) {
-  final Map<LibraryClass, int> builtInClasses = {};
-
-  if (classNamesToMonitor != null && classNamesToMonitor.isNotEmpty) {
-    print(
-      'WARNING: Remove classNamesToMonitor before PR submission. '
-      '$classNamesToMonitor',
-    );
-  }
-
-  // Construct all the classes in the snapshot.
-  final classes = List<HeapGraphClassLive?>.filled(graph.classes.length, null);
-  for (int i = 0; i < graph.classes.length; i++) {
-    final HeapSnapshotClass c = graph.classes[i];
-
-    final className = c.name;
-    // Remember builtin classes classId e.g., bool, String (_OneByteString), etc.
-    // The classId is the index into the graph.classes list.
-    final libraryClass = LibraryClass('${c.libraryUri}', c.name);
-    // It's a exact match libraryName,className once we match the classId drives
-    // the class matching.
-    if (predefinedClasses.containsKey(libraryClass)) {
-      builtInClasses.putIfAbsent(libraryClass, () => i);
-    }
-
-    // Debugging code to monitor a particular class.  ClassesToMonitor should be
-    // empty before commiting PRs.
-    if (classNamesToMonitor != null &&
-        classNamesToMonitor.isNotEmpty &&
-        classNamesToMonitor.contains(className)) {
-      print('WARNING: class $className is monitored.');
-      _monitorClasses.putIfAbsent(i, () => className);
-    }
-
-    classes[i] = HeapGraphClassLive(c);
-  }
-
-  // Pre-allocate the number of objects in the snapshot.
-  final elements =
-      List<HeapGraphElementLive?>.filled(graph.objects.length, null);
-
-  // Construct all objects.
-  for (int i = 0; i < graph.objects.length; i++) {
-    final HeapSnapshotObject o = graph.objects[i];
-    elements[i] = HeapGraphElementLive(o);
-  }
-
-  // Associate each object with a Class.
-  for (int i = 0; i < graph.objects.length; i++) {
-    final HeapSnapshotObject o = graph.objects[i];
-    final HeapGraphElementLive converted = elements[i]!;
-
-    // New style snapshot class index is zero based.
-    if (o.classId == 0) {
-      // classId of 0 is a sentinel.
-      converted.theClass = HeapGraph.classSentinel;
-    } else {
-      // Support for new snapshot class index is zero based (starts at
-      // zero) and the previous snapshot classId is 1-based index.
-      final classId = o.classId;
-      monitorClass(classId: classId);
-      converted.theClass = classes[classId];
-    }
-
-    // Lazily compute the references.
-    converted.referencesFiller = () {
-      for (int refId in o.references) {
-        HeapGraphElement ref;
-        if (refId == 0) {
-          ref = HeapGraph.elementSentinel;
-        } else {
-          ref = elements[refId]!;
-        }
-        converted.references!.add(ref);
-      }
-    };
-  }
-
-  final snapshotExternals = graph.externalProperties;
-
-  // Pre-allocate the number of external objects in the snapshot.
-  final externals =
-      List<HeapGraphExternalLive?>.filled(snapshotExternals.length, null);
-
-  // Construct all external objects and link to its live element.
-  for (int index = 0; index < snapshotExternals.length; index++) {
-    final snapshotObject = snapshotExternals[index];
-    final liveElement = elements[snapshotObject.object]!;
-    externals[index] = HeapGraphExternalLive(snapshotObject, liveElement);
-  }
-
-  return HeapGraph(
-    filterConfig,
-    builtInClasses,
-    classes,
-    elements,
-    externals,
-  );
-}
-
-class HeapGraph {
-  HeapGraph(
-    this.filterConfig,
-    this.builtInClasses,
-    this.classes,
-    this.elements,
-    this.externals,
-  );
-
-  final FilterConfig filterConfig;
-
-  bool _instancesComputed = false;
-
-  bool get instancesComputed => _instancesComputed;
-
-  /// Known built-in classes.
-  final Map<LibraryClass, int> builtInClasses;
-
-  /// Sentinel Class, all class sentinels point to this object.
-  static HeapGraphClassSentinel classSentinel = HeapGraphClassSentinel();
-
-  /// Indexed by classId.
-  final List<HeapGraphClassLive?> classes;
-
-  /// Sentinel Object, all object sentinels point to this object.
-  static HeapGraphElementSentinel elementSentinel = HeapGraphElementSentinel();
-
-  /// Index by objectId.
-  final List<HeapGraphElementLive?> elements;
-
-  /// Index by objectId of all external properties
-  List<HeapGraphExternalLive?> externals;
-
-  /// Group all classes by libraries (key is library, value are classes).
-  /// This is the entire set of objects (no filter applied).
-  final Map<String, Set<HeapGraphClassLive>> rawGroupByLibrary = {};
-
-  /// Group all classes by libraries (key is library, value is classes).
-  /// Filtering out objects that match a given filter. This is always a
-  /// subset of rawGroupByLibrary.
-  final Map<String, Set<HeapGraphClassLive>> groupByLibrary = {};
-
-  /// Group all instances by class (key is class name, value are class
-  /// instances).  This is the entire set of objects (no filter applied).
-  final Map<String, Set<HeapGraphElementLive?>> rawGroupByClass = {};
-
-  /// Group all instances by class (key is class name, value are class
-  /// instances).  Filtering out objects that match a given filter. This
-  /// is always a subset of rawGroupByClass.
-  final Map<String, Set<HeapGraphElementLive>> groupByClass = {};
-
-  /// Group of instances by filtered out classes (key is class name, value
-  /// are instances). These are the instances not in groupByClass, together
-  /// filteredElements and groupByClass are equivalent to rawGroupByClass.
-  final Map<String, Set<HeapGraphElementLive>> filteredElements = {};
-
-  /// Group of libraries by filtered out classes (key is library name, value
-  /// are classes). These are the libraries not in groupByLibrary, together
-  /// filteredLibraries and groupByLibrary are equivalent to rawGroupByLibrary.
-  final Map<String, Set<HeapGraphClassLive>> filteredLibraries = {};
-
-  /// Normalize the library name. Library is a Uri that contains
-  /// the schema e.g., 'dart' or 'package' and pathSegments. The
-  /// segments are paths to a dart file.  Without simple normalization
-  /// 100s maybe 1000s of libraries would be displayed to the
-  /// developer.  Normalizing takes the schema and the first part
-  /// of the path e.g., dart:core, package:flutter, etc. Hopefully,
-  /// this is not too chunky but better than no normalization.  Also,
-  /// the empty library is returned as src e.g., lib/src
-  String normalizeLibraryName(HeapSnapshotClass theClass) {
-    final uri = theClass.libraryUri;
-    final scheme = uri.scheme;
-
-    if (scheme == 'package' || scheme == 'dart') {
-      return '$scheme:${uri.pathSegments[0]}';
-    }
-
-    assert(theClass.libraryName.isEmpty);
-    return 'src';
-  }
-
-  void computeRawGroups() {
-    // Only compute once.
-    if (rawGroupByLibrary.isNotEmpty || rawGroupByClass.isNotEmpty) return;
-
-    for (final c in classes) {
-      if (c == null) continue;
-
-      final sb = StringBuffer();
-
-      final libraryKey = normalizeLibraryName(c.origin);
-
-      // Collect classes for each library (group by library).
-      sb.write(libraryKey);
-      final librarySbToString = sb.toString();
-      rawGroupByLibrary[librarySbToString] ??= <HeapGraphClassLive>{};
-      rawGroupByLibrary[librarySbToString]!.add(c);
-      sb.clear();
-
-      // Collect instances for each class (group by class)
-      for (final instance in c.getInstances(this)) {
-        sb.write(c.name);
-        c.instancesTotalShallowSizes += instance.origin.shallowSize;
-        final classSbToString = sb.toString();
-        rawGroupByClass[classSbToString] ??= <HeapGraphElementLive?>{};
-        rawGroupByClass[classSbToString]!.add(instance);
-        sb.clear();
-      }
-    }
-  }
-
-  void computeFilteredGroups() {
-    // Clone groupByClass from raw group.
-    groupByClass.clear();
-    rawGroupByClass.forEach((key, value) {
-      groupByClass[key] = value.cast<HeapGraphElementLive>().toSet();
-    });
-
-    // Prune classes that are private or have zero instances.
-    filteredElements.clear();
-    groupByClass.removeWhere((className, instances) {
-      final remove = (filterConfig.filterZeroInstances.value &&
-              instances.isEmpty) ||
-          (filterConfig.filterPrivateClasses.value && isPrivate(className)) ||
-          className == internalClassName;
-      if (remove) {
-        filteredElements.putIfAbsent(className, () => instances);
-      }
-
-      return remove;
-    });
-
-    // Clone groupByLibrary from raw group.
-    groupByLibrary.clear();
-    rawGroupByLibrary.forEach((key, value) {
-      groupByLibrary[key] = value.cast<HeapGraphClassLive>().toSet();
-    });
-
-    // Prune libraries if all their classes are private or have zero instances.
-    filteredLibraries.clear();
-
-    groupByLibrary.removeWhere((libraryName, classes) {
-      classes.removeWhere((actual) {
-        final result = (filterConfig.filterZeroInstances.value &&
-                actual.getInstances(this).isEmpty) ||
-            (filterConfig.filterPrivateClasses.value &&
-                isPrivate(actual.name)) ||
-            actual.name == internalClassName;
-        return result;
-      });
-
-      final result =
-          (filterConfig.libraryFilters.isLibraryFiltered(libraryName)) ||
-              filterConfig.filterLibraryNoInstances.value && classes.isEmpty;
-      if (result) {
-        filteredLibraries.putIfAbsent(libraryName, () => classes);
-      }
-
-      return result;
-    });
-  }
-
-  // TODO(terry): Need dominator graph for flow.
-  /// Compute all instances, needed for retained space.
-  void computeInstancesForClasses() {
-    if (!instancesComputed) {
-      for (final instance in elements) {
-        instance?.theClass!.addInstance(instance);
-      }
-
-      _instancesComputed = true;
-    }
-  }
-}
-
-abstract class HeapGraphElement {
-  /// Outbound references, i.e. this element points to elements in this list.
-  List<HeapGraphElement>? _references;
-
-  void Function()? referencesFiller;
-
-  List<HeapGraphElement>? get references {
-    if (_references == null && referencesFiller != null) {
-      _references = [];
-      referencesFiller!();
-    }
-    return _references;
-  }
-
-  bool get isSentinel;
-}
-
-/// Object marked for removal on next GC.
-class HeapGraphElementSentinel extends HeapGraphElement {
-  @override
-  bool get isSentinel => true;
-
-  @override
-  String toString() => 'HeapGraphElementSentinel';
-}
-
-/// Live element.
-class HeapGraphElementLive extends HeapGraphElement {
-  HeapGraphElementLive(this.origin);
-
-  final HeapSnapshotObject origin;
-  HeapGraphClass? theClass;
-
-  @override
-  bool get isSentinel => false;
-
-  HeapGraphElement? getField(String name) {
-    if (theClass is HeapGraphClassLive) {
-      final HeapGraphClassLive c = theClass as HeapGraphClassLive;
-      for (HeapSnapshotField field in c.origin.fields) {
-        if (field.name == name) {
-          return references![field.index];
-        }
-      }
-    }
-    return null;
-  }
-
-  List<MapEntry<String, HeapGraphElement>> getFields() {
-    final List<MapEntry<String, HeapGraphElement>> result = [];
-    if (theClass is HeapGraphClassLive) {
-      final HeapGraphClassLive c = theClass as HeapGraphClassLive;
-      for (final field in c.origin.fields) {
-        if (field.index < references!.length) {
-          result.add(MapEntry(field.name, references![field.index]));
-        } else {
-          // TODO(polinach): figure out what to do if index is out of range.
-          // See https://github.com/flutter/devtools/issues/3629 for details.
-          // log(
-          //   'ERROR Field Range: name=${field.name},index=${field.index}',
-          //   LogLevel.error,
-          // );
-        }
-      }
-    }
-    return result;
-  }
-
-  @override
-  String toString() {
-    if (origin.data is HeapSnapshotObjectNoData) {
-      return 'Instance of $theClass';
-    }
-    if (origin.data is HeapSnapshotObjectLengthData) {
-      final HeapSnapshotObjectLengthData data = origin.data;
-      return 'Instance of $theClass length = ${data.length}';
-    }
-    return 'Instance of $theClass; data: \'${origin.data}\'';
-  }
-}
-
-/// Live ExternalProperty.
-class HeapGraphExternalLive extends HeapGraphElement {
-  HeapGraphExternalLive(this.externalProperty, this.live);
-
-  final HeapSnapshotExternalProperty externalProperty;
-  final HeapGraphElementLive live;
-
-  @override
-  bool get isSentinel => false;
-
-  @override
-  String toString() {
-    if (live.origin.data is HeapSnapshotObjectNoData) {
-      return 'Instance of ${live.theClass}';
-    }
-    if (live.origin.data is HeapSnapshotObjectLengthData) {
-      final HeapSnapshotObjectLengthData data = live.origin.data;
-      return 'Instance of ${live.theClass} length = ${data.length}';
-    }
-    return 'Instance of ${live.theClass}; data: \'${live.origin.data}\'';
-  }
-}
-
-abstract class HeapGraphClass {
-  final List<HeapGraphElementLive> _instances = [];
-
-  int instancesTotalShallowSizes = 0;
-
-  void addInstance(HeapGraphElementLive instance) {
-    _instances.add(instance);
-  }
-
-  List<HeapGraphElementLive> getInstances(HeapGraph? graph) {
-    // TODO(polinach): this code never executes and should be reviewed and
-    // fixed.
-    // ignore: unnecessary_null_comparison
-    if (_instances == null) {
-      final len = graph?.elements.length ?? 0;
-      for (var i = 0; i < len; i++) {
-        final HeapGraphElementLive? converted = graph!.elements[i];
-        if (converted != null && converted.theClass == this) {
-          _instances.add(converted);
-        }
-      }
-    }
-    return _instances;
-  }
-
-  /// Quick short-circuit to return real size of null implies '--' yet to be
-  /// computed or N/A.
-  // TODO(polinach): _instances is never null. So, this code
-  // should be reviewed and fixed.
-  // ignore: unnecessary_null_comparison
-  int? get instancesCount => _instances == null ? null : _instances.length;
-}
-
-class HeapGraphClassSentinel extends HeapGraphClass {
-  @override
-  String toString() => 'HeapGraphClassSentinel';
-}
-
-class HeapGraphClassLive extends HeapGraphClass {
-  HeapGraphClassLive(this.origin);
-
-  final HeapSnapshotClass origin;
-
-  String get name => origin.name;
-
-  Uri get libraryUri => origin.libraryUri;
-
-  LibraryClass get fullQualifiedName =>
-      LibraryClass(libraryUri.toString(), name);
-
-  @override
-  String toString() => name;
-}
diff --git a/packages/devtools_app/lib/src/screens/memory/memory_heap_tree_view.dart b/packages/devtools_app/lib/src/screens/memory/memory_heap_tree_view.dart
deleted file mode 100644
index bb54ef5..0000000
--- a/packages/devtools_app/lib/src/screens/memory/memory_heap_tree_view.dart
+++ /dev/null
@@ -1,1618 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'dart:async';
-import 'dart:math';
-
-import 'package:collection/collection.dart' show IterableExtension;
-import 'package:flutter/material.dart';
-import 'package:intl/intl.dart';
-
-import '../../analytics/analytics.dart' as ga;
-import '../../analytics/constants.dart' as analytics_constants;
-import '../../config_specific/logger/logger.dart' as logger;
-import '../../primitives/auto_dispose_mixin.dart';
-import '../../primitives/utils.dart';
-import '../../shared/common_widgets.dart';
-import '../../shared/globals.dart';
-import '../../shared/split.dart';
-import '../../shared/table/table.dart';
-import '../../shared/table/table_data.dart';
-import '../../shared/theme.dart';
-import '../../shared/utils.dart';
-import '../../ui/search.dart';
-import '../../ui/tab.dart';
-import 'memory_analyzer.dart';
-import 'memory_controller.dart';
-import 'memory_filter.dart';
-import 'memory_graph_model.dart';
-import 'memory_heap_treemap.dart';
-import 'memory_instance_tree_view.dart';
-import 'memory_snapshot_models.dart';
-import 'panes/allocation_profile/allocation_profile_table_view.dart';
-import 'panes/allocation_tracing/allocation_profile_tracing_view.dart';
-import 'panes/diff/diff_pane.dart';
-import 'panes/leaks/leaks_pane.dart';
-import 'primitives/memory_utils.dart';
-
-const memorySearchFieldKeyName = 'MemorySearchFieldKey';
-
-@visibleForTesting
-final memorySearchFieldKey = GlobalKey(debugLabel: memorySearchFieldKeyName);
-
-enum SnapshotStatus {
-  none,
-  streaming,
-  graphing,
-  grouping,
-  done,
-}
-
-enum WildcardMatch {
-  exact,
-  startsWith,
-  endsWith,
-  contains,
-}
-
-/// If no wildcard then exact match.
-/// *NNN - ends with NNN
-/// NNN* - starts with NNN
-/// NNN*ZZZ - starts with NNN and ends with ZZZ
-const knowClassesToAnalyzeForImages = <WildcardMatch, List<String>>{
-  // Anything that contains the phrase:
-  WildcardMatch.contains: [
-    'Image',
-  ],
-
-  // Anything that starts with:
-  WildcardMatch.startsWith: [],
-
-  // Anything that exactly matches:
-  WildcardMatch.exact: [
-    '_Int32List',
-    // 32-bit devices e.g., emulators, Pixel 2, raw images as Int32List.
-    '_Int64List',
-    // 64-bit devices e.g., Pixel 3 XL, raw images as Int64List.
-    'FrameInfos',
-  ],
-
-  // Anything that ends with:
-  WildcardMatch.endsWith: [],
-};
-
-/// RegEx expressions to handle the WildcardMatches:
-///     Ends with Image:      \[_A-Za-z0-9_]*Image\$
-///     Starts with Image:    ^Image
-///     Contains Image:       Image
-///     Extact Image:         ^Image$
-String buildRegExs(Map<WildcardMatch, List<String>> matchingCriteria) {
-  final resultRegEx = StringBuffer();
-  matchingCriteria.forEach((key, value) {
-    if (value.isNotEmpty) {
-      final name = value;
-      late String regEx;
-      // TODO(terry): Need to handle $ for identifier names e.g.,
-      //              $FOO is a valid identifier.
-      switch (key) {
-        case WildcardMatch.exact:
-          regEx = '^${name.join("\$|^")}\$';
-          break;
-        case WildcardMatch.startsWith:
-          regEx = '^${name.join("|^")}';
-          break;
-        case WildcardMatch.endsWith:
-          regEx = '^\[_A-Za-z0-9]*${name.join("\|[_A-Za-z0-9]*")}\$';
-          break;
-        case WildcardMatch.contains:
-          regEx = '${name.join("|")}';
-          break;
-        default:
-          assert(false, 'buildRegExs: Unhandled WildcardMatch');
-      }
-      resultRegEx.write(resultRegEx.isEmpty ? '($regEx' : '|$regEx');
-    }
-  });
-
-  resultRegEx.write(')');
-  return resultRegEx.toString();
-}
-
-final String knownClassesRegExs = buildRegExs(knowClassesToAnalyzeForImages);
-
-@visibleForTesting
-class MemoryScreenKeys {
-  static const searchButton = Key('Snapshot Search');
-  static const filterButton = Key('Snapshot Filter');
-  static const leaksTab = Key('Leaks Tab');
-  static const dartHeapTableProfileTab = Key('Dart Heap Profile Tab');
-  static const dartHeapAllocationTracingTab =
-      Key('Dart Heap Allocation Tracing Tab');
-  static const diffTab = Key('Diff Tab');
-}
-
-class HeapTreeView extends StatefulWidget {
-  const HeapTreeView(
-    this.controller,
-  );
-
-  final MemoryController controller;
-
-  @override
-  _HeapTreeViewState createState() => _HeapTreeViewState();
-}
-
-class _HeapTreeViewState extends State<HeapTreeView>
-    with
-        AutoDisposeMixin,
-        ProvidedControllerMixin<MemoryController, HeapTreeView>,
-        SearchFieldMixin<HeapTreeView>,
-        TickerProviderStateMixin {
-  static const _gaPrefix = 'memoryTab';
-
-  late List<Tab> _tabs;
-  late TabController _tabController;
-  late Set<Key> _searchableTabs;
-  final ValueNotifier<int> _currentTab = ValueNotifier(0);
-
-  Widget? snapshotDisplay;
-
-  /// Used to detect a spike in memory usage.
-  MovingAverage heapMovingAverage = MovingAverage(averagePeriod: 100);
-
-  /// Number of seconds between auto snapshots because RSS is exceeded.
-  static const maxRSSExceededDurationSecs = 30;
-
-  static const minPercentIncrease = 30;
-
-  /// Timestamp of HeapSample that caused auto snapshot.
-  int? spikeSnapshotTime;
-
-  /// Timestamp when RSS exceeded auto snapshot.
-  int rssSnapshotTime = 0;
-
-  /// Total memory that caused last snapshot.
-  int lastSnapshotMemoryTotal = 0;
-
-  late bool treeMapVisible;
-
-  late AnimationController _animation;
-
-  @override
-  void initState() {
-    super.initState();
-
-    _animation = _setupBubbleAnimationController();
-  }
-
-  void _initTabs() {
-    _tabs = [
-      DevToolsTab.create(
-        key: MemoryScreenKeys.dartHeapTableProfileTab,
-        tabName: 'Profile',
-        gaPrefix: _gaPrefix,
-      ),
-      DevToolsTab.create(
-        key: MemoryScreenKeys.dartHeapAllocationTracingTab,
-        tabName: 'Allocation Tracing',
-        gaPrefix: _gaPrefix,
-      ),
-      DevToolsTab.create(
-        key: MemoryScreenKeys.diffTab,
-        gaPrefix: _gaPrefix,
-        tabName: 'Diff',
-      ),
-      if (widget.controller.shouldShowLeaksTab.value)
-        DevToolsTab.create(
-          key: MemoryScreenKeys.leaksTab,
-          gaPrefix: _gaPrefix,
-          tabName: 'Leaks',
-        ),
-    ];
-
-    // TODO(polina-c): clean up analysis tab and search.
-    _searchableTabs = {};
-    _tabController = TabController(length: _tabs.length, vsync: this);
-    _tabController.addListener(_onTabChanged);
-  }
-
-  void _onTabChanged() => _currentTab.value = _tabController.index;
-
-  @override
-  void didChangeDependencies() {
-    super.didChangeDependencies();
-    if (!initController()) return;
-
-    cancelListeners();
-
-    _initTabs();
-
-    addAutoDisposeListener(controller.shouldShowLeaksTab, () {
-      setState(() {
-        _initTabs();
-      });
-    });
-
-    addAutoDisposeListener(controller.selectedSnapshotNotifier, () {
-      setState(() {
-        controller.computeAllLibraries(rebuild: true);
-      });
-    });
-
-    addAutoDisposeListener(controller.selectionSnapshotNotifier, () {
-      setState(() {
-        controller.computeAllLibraries(rebuild: true);
-      });
-    });
-
-    addAutoDisposeListener(controller.filterNotifier, () {
-      setState(() {
-        controller.computeAllLibraries(rebuild: true);
-      });
-    });
-
-    addAutoDisposeListener(controller.leafSelectedNotifier, () {
-      setState(() {
-        controller.computeRoot();
-      });
-    });
-
-    addAutoDisposeListener(controller.leafAnalysisSelectedNotifier, () {
-      setState(() {
-        controller.computeAnalysisInstanceRoot();
-      });
-    });
-
-    addAutoDisposeListener(controller.searchNotifier, () {
-      controller.closeAutoCompleteOverlay();
-      controller..setCurrentHoveredIndexValue(0);
-    });
-
-    addAutoDisposeListener(controller.searchAutoCompleteNotifier, () {
-      controller.handleAutoCompleteOverlay(
-        context: context,
-        searchFieldKey: memorySearchFieldKey,
-        onTap: selectTheMatch,
-      );
-    });
-
-    addAutoDisposeListener(controller.memoryTimeline.sampleAddedNotifier, () {
-      autoSnapshot();
-    });
-
-    treeMapVisible = controller.treeMapVisible.value;
-    addAutoDisposeListener(controller.treeMapVisible, () {
-      setState(() {
-        treeMapVisible = controller.treeMapVisible.value;
-      });
-    });
-  }
-
-  @override
-  void dispose() {
-    _animation.dispose();
-    _tabController
-      ..removeListener(_onTabChanged)
-      ..dispose();
-
-    super.dispose();
-  }
-
-  /// Enable to output debugging information for auto-snapshot.
-  /// WARNING: Do not checkin with this flag set to true.
-  final debugSnapshots = false;
-
-  /// Detect spike in memory usage if so do an automatic snapshot.
-  void autoSnapshot() {
-    if (!preferences.memory.autoSnapshotEnabled.value) return;
-    final heapSample = controller.memoryTimeline.sampleAddedNotifier.value!;
-    final heapSum = heapSample.external + heapSample.used;
-    heapMovingAverage.add(heapSum);
-
-    final dateTimeFormat = DateFormat('HH:mm:ss.SSS');
-    final startDateTime = dateTimeFormat
-        .format(DateTime.fromMillisecondsSinceEpoch(heapSample.timestamp));
-
-    if (debugSnapshots) {
-      debugLogger(
-        'AutoSnapshot $startDateTime heapSum=$heapSum, '
-        'first=${heapMovingAverage.dataSet.first}, '
-        'mean=${heapMovingAverage.mean}',
-      );
-    }
-
-    bool takeSnapshot = false;
-    final sampleTime = Duration(milliseconds: heapSample.timestamp);
-
-    final rssExceeded = heapSum > heapSample.rss;
-    if (rssExceeded) {
-      final increase = heapSum - lastSnapshotMemoryTotal;
-      final rssPercentIncrease = lastSnapshotMemoryTotal > 0
-          ? increase / lastSnapshotMemoryTotal * 100
-          : 0;
-      final rssTime = Duration(milliseconds: rssSnapshotTime);
-      // Number of seconds since last snapshot happens because of RSS exceeded.
-      // Reduce rapid fire snapshots.
-      final rssSnapshotPeriod = (sampleTime - rssTime).inSeconds;
-      if (rssSnapshotPeriod > maxRSSExceededDurationSecs) {
-        // minPercentIncrease larger than previous auto RSS snapshot then
-        // take another snapshot.
-        if (rssPercentIncrease > minPercentIncrease) {
-          rssSnapshotTime = heapSample.timestamp;
-          lastSnapshotMemoryTotal = heapSum;
-
-          takeSnapshot = true;
-          debugLogger(
-            'AutoSnapshot - RSS exceeded '
-            '($rssPercentIncrease% increase) @ $startDateTime.',
-          );
-        }
-      }
-    }
-
-    if (!takeSnapshot && heapMovingAverage.hasSpike()) {
-      final snapshotTime =
-          Duration(milliseconds: spikeSnapshotTime ?? heapSample.timestamp);
-      spikeSnapshotTime = heapSample.timestamp;
-      takeSnapshot = true;
-      logger.log(
-        'AutoSnapshot - memory spike @ $startDateTime} '
-        'last snapshot ${(sampleTime - snapshotTime).inSeconds} seconds ago.',
-      );
-      debugLogger(
-        '               '
-        'heap @ last snapshot = $lastSnapshotMemoryTotal, '
-        'heap total=$heapSum, RSS=${heapSample.rss}',
-      );
-    }
-
-    if (takeSnapshot) {
-      assert(!heapMovingAverage.isDipping());
-      // Reset moving average for next spike.
-      heapMovingAverage.clear();
-      // TODO(terry): Should get the real sum of the snapshot not the current memory.
-      //              Snapshot can take a bit and could be a lag.
-      lastSnapshotMemoryTotal = heapSum;
-      _takeHeapSnapshot(userGenerated: false);
-    } else if (heapMovingAverage.isDipping()) {
-      // Reset the two things we're tracking spikes and RSS exceeded.
-      heapMovingAverage.clear();
-      lastSnapshotMemoryTotal = heapSum;
-    }
-  }
-
-  SnapshotStatus snapshotState = SnapshotStatus.none;
-
-  bool get _isSnapshotRunning =>
-      snapshotState != SnapshotStatus.done &&
-      snapshotState != SnapshotStatus.none;
-
-  bool get _isSnapshotStreaming => snapshotState == SnapshotStatus.streaming;
-
-  bool get _isSnapshotGraphing => snapshotState == SnapshotStatus.graphing;
-
-  bool get _isSnapshotGrouping => snapshotState == SnapshotStatus.grouping;
-
-  bool get _isSnapshotComplete => snapshotState == SnapshotStatus.done;
-
-  @override
-  Widget build(BuildContext context) {
-    final themeData = Theme.of(context);
-
-    if (_isSnapshotRunning) {
-      snapshotDisplay = Column(
-        mainAxisAlignment: MainAxisAlignment.center,
-        children: [
-          const SizedBox(height: 50.0),
-          snapshotDisplay = const CircularProgressIndicator(),
-          const SizedBox(height: denseSpacing),
-          Text(
-            _isSnapshotStreaming
-                ? 'Processing...'
-                : _isSnapshotGraphing
-                    ? 'Graphing...'
-                    : _isSnapshotGrouping
-                        ? 'Grouping...'
-                        : _isSnapshotComplete
-                            ? 'Done'
-                            : '...',
-          ),
-        ],
-      );
-    } else if (controller.snapshotByLibraryData != null) {
-      snapshotDisplay =
-          treeMapVisible ? const MemoryHeapTreemap() : MemoryHeapTable();
-    } else {
-      snapshotDisplay = null;
-    }
-
-    return Column(
-      children: [
-        const SizedBox(height: defaultSpacing),
-        ValueListenableBuilder<int>(
-          valueListenable: _currentTab,
-          builder: (context, index, _) => Row(
-            mainAxisAlignment: MainAxisAlignment.spaceBetween,
-            children: [
-              TabBar(
-                labelColor: themeData.textTheme.bodyLarge!.color,
-                isScrollable: true,
-                controller: _tabController,
-                tabs: _tabs,
-              ),
-              if (_searchableTabs.contains(_tabs[index].key))
-                _buildSearchFilterControls(),
-            ],
-          ),
-        ),
-        const Divider(),
-        Expanded(
-          child: TabBarView(
-            physics: defaultTabBarViewPhysics,
-            controller: _tabController,
-            children: [
-              // Profile Tab
-              KeepAliveWrapper(
-                child: AllocationProfileTableView(
-                  controller: controller.allocationProfileController,
-                ),
-              ),
-              const KeepAliveWrapper(
-                child: AllocationProfileTracingView(),
-              ),
-              // Diff tab.
-              KeepAliveWrapper(
-                child: DiffPane(
-                  diffController: controller.diffPaneController,
-                ),
-              ),
-              // Leaks tab.
-              if (controller.shouldShowLeaksTab.value)
-                const KeepAliveWrapper(child: LeaksPane()),
-            ],
-          ),
-        ),
-      ],
-    );
-  }
-
-  Widget buildSnapshotTables(Widget? snapshotDisplay) {
-    if (snapshotDisplay == null) {
-      // Display help text about how to collect data.
-      return OutlineDecoration(
-        child: Center(
-          child: Row(
-            mainAxisAlignment: MainAxisAlignment.center,
-            children: const [
-              Text('Click the take heap snapshot button '),
-              Icon(Icons.camera),
-              Text(' to collect a graph of memory objects.'),
-            ],
-          ),
-        ),
-      );
-    }
-
-    final rightSideTable = controller.isLeafSelected
-        ? InstanceTreeView()
-        : controller.isAnalysisLeafSelected
-            ? Expanded(child: AnalysisInstanceViewTable())
-            : helpScreen();
-
-    return treeMapVisible
-        ? snapshotDisplay
-        : Split(
-            initialFractions: const [0.5, 0.5],
-            minSizes: const [300, 300],
-            axis: Axis.horizontal,
-            children: [
-              // TODO(terry): Need better focus handling between 2 tables & up/down
-              //              arrows in the right-side field instance view table.
-              snapshotDisplay,
-              OutlineDecoration(child: rightSideTable),
-            ],
-          );
-  }
-
-  Widget tableExample(IconData? iconData, String entry) {
-    final themeData = Theme.of(context);
-    return Row(
-      mainAxisAlignment: MainAxisAlignment.center,
-      children: [
-        iconData == null
-            ? Text(' ', style: themeData.fixedFontStyle)
-            : Icon(iconData),
-        Text(entry, style: themeData.fixedFontStyle),
-      ],
-    );
-  }
-
-  Widget helpScreen() {
-    return Column(
-      mainAxisAlignment: MainAxisAlignment.center,
-      children: [
-        const Text(
-          'Click a leaf node instance of a class to\n'
-          'inspect the fields of that instance e.g.,',
-        ),
-        const SizedBox(height: defaultSpacing),
-        tableExample(Icons.expand_more, 'dart:collection'),
-        tableExample(Icons.expand_more, 'SplayTreeMap'),
-        const SizedBox(height: denseRowSpacing),
-        tableExample(null, 'Instance 0'),
-      ],
-    );
-  }
-
-  @visibleForTesting
-  static const groupByMenuButtonKey = Key('Group By Menu Button');
-  @visibleForTesting
-  static const groupByMenuItem = Key('Group By Menu Item');
-  @visibleForTesting
-  static const groupByKey = Key('Group By');
-
-  Widget _groupByDropdown(TextTheme textTheme) {
-    final _groupByTypes = [
-      MemoryController.groupByLibrary,
-      MemoryController.groupByClass,
-      MemoryController.groupByInstance,
-    ].map<DropdownMenuItem<String>>(
-      (
-        String value,
-      ) {
-        return DropdownMenuItem<String>(
-          key: groupByMenuItem,
-          value: value,
-          child: Text(
-            'Group by $value',
-            key: groupByKey,
-          ),
-        );
-      },
-    ).toList();
-
-    return DropdownButtonHideUnderline(
-      child: DropdownButton<String>(
-        key: groupByMenuButtonKey,
-        style: textTheme.bodyMedium,
-        value: controller.groupingBy.value,
-        onChanged: (String? newValue) {
-          setState(
-            () {
-              controller.selectedLeaf = null;
-              controller.groupingBy.value = newValue!;
-              if (controller.snapshots.isNotEmpty) {
-                unawaited(doGroupBy());
-              }
-            },
-          );
-        },
-        items: _groupByTypes,
-      ),
-    );
-  }
-
-  // TODO(polina-c): cleanup unused code.
-  // ignore: unused_element
-  Widget _buildSnapshotControls(TextTheme textTheme) {
-    return SizedBox(
-      height: defaultButtonHeight,
-      child: Row(
-        children: [
-          IconLabelButton(
-            tooltip: 'Take a memory profile snapshot',
-            icon: Icons.camera,
-            label: 'Take Heap Snapshot',
-            onPressed: _isSnapshotRunning ? null : _takeHeapSnapshot,
-          ),
-          const SizedBox(width: defaultSpacing),
-          Row(
-            mainAxisSize: MainAxisSize.min,
-            children: [
-              const Text('Treemap'),
-              Switch(
-                value: treeMapVisible,
-                onChanged: controller.snapshotByLibraryData != null
-                    ? (value) {
-                        controller.toggleTreeMapVisible(value);
-                      }
-                    : null,
-              ),
-            ],
-          ),
-          if (!treeMapVisible) ...[
-            const SizedBox(width: defaultSpacing),
-            _groupByDropdown(textTheme),
-            const SizedBox(width: defaultSpacing),
-            // TODO(terry): Mechanism to handle expand/collapse on both tables
-            // objects/fields. Maybe notion in table?
-            ExpandAllButton(
-              onPressed: () {
-                ga.select(
-                  analytics_constants.memory,
-                  analytics_constants.expandAll,
-                );
-                if (snapshotDisplay is MemoryHeapTable) {
-                  controller.groupByTreeTable.dataRoots.every((element) {
-                    element.expandCascading();
-                    return true;
-                  });
-                }
-                // All nodes expanded - signal tree state  changed.
-                controller.treeChanged();
-              },
-            ),
-            const SizedBox(width: denseSpacing),
-            CollapseAllButton(
-              onPressed: () {
-                ga.select(
-                  analytics_constants.memory,
-                  analytics_constants.collapseAll,
-                );
-                if (snapshotDisplay is MemoryHeapTable) {
-                  controller.groupByTreeTable.dataRoots.every((element) {
-                    element.collapseCascading();
-                    return true;
-                  });
-                  if (controller.instanceFieldsTreeTable != null) {
-                    // We're collapsing close the fields table.
-                    controller.selectedLeaf = null;
-                  }
-                  // All nodes collapsed - signal tree state changed.
-                  controller.treeChanged();
-                }
-              },
-            ),
-          ],
-        ],
-      ),
-    );
-  }
-
-  AnimationController _setupBubbleAnimationController() {
-    // Setup animation controller to handle the update bubble.
-    const animationDuration = Duration(milliseconds: 500);
-    final bubbleAnimation = AnimationController(
-      duration: animationDuration,
-      reverseDuration: animationDuration,
-      upperBound: 15.0,
-      vsync: this,
-    );
-
-    bubbleAnimation.addStatusListener(_animationStatusListener);
-
-    return bubbleAnimation;
-  }
-
-  void _animationStatusListener(AnimationStatus status) {
-    if (status == AnimationStatus.completed) {
-      // Reverse from larger bubble back to normal bubble radius.
-      _animation.reverse();
-    }
-  }
-
-  Timer? removeUpdateBubble;
-
-  Widget textWidgetWithUpdateCircle(
-    String text, {
-    TextStyle? style,
-    double? size,
-  }) {
-    final textWidth = textWidgetWidth(text, style: style);
-
-    return Stack(
-      children: [
-        Positioned(
-          child: Container(
-            width: textWidth + 10,
-            child: Text(text, style: style),
-          ),
-        ),
-        Positioned(
-          right: 0,
-          child: Container(
-            alignment: Alignment.topRight,
-            width: size,
-            height: size,
-            decoration: BoxDecoration(
-              shape: BoxShape.circle,
-              color: Colors.blue[400],
-            ),
-            child: const Icon(Icons.fiber_manual_record, size: 0),
-          ),
-        ),
-      ],
-    );
-  }
-
-  static const maxWidth = 800.0;
-
-  double textWidgetWidth(String message, {TextStyle? style}) {
-    // Longest message must fit in this width.
-    const constraints = BoxConstraints(
-      maxWidth: maxWidth,
-    );
-
-    // TODO(terry): Is there a better (less heavyweight) way of computing text
-    //              width than using the widget pipeline?
-    final richTextWidget = Text.rich(TextSpan(text: message), style: style)
-        .build(context) as RichText;
-    final renderObject = richTextWidget.createRenderObject(context);
-    renderObject.layout(constraints);
-    final boxes = renderObject.getBoxesForSelection(
-      TextSelection(
-        baseOffset: 0,
-        extentOffset: TextSpan(text: message).toPlainText().length,
-      ),
-    );
-
-    final textWidth = boxes.last.right;
-
-    if (textWidth > maxWidth) {
-      // TODO(terry): If message > 800 pixels in width (not possible
-      //              today) but could be more robust.
-      logger.log(
-        'Computed text width > $maxWidth ($textWidth)\nmessage=$message.',
-        logger.LogLevel.warning,
-      );
-    }
-
-    return textWidth;
-  }
-
-  /// Match, found,  select it and process via ValueNotifiers.
-  void selectTheMatch(String foundName) {
-    setState(() {
-      if (snapshotDisplay is MemoryHeapTable) {
-        controller.groupByTreeTable.dataRoots.every((element) {
-          element.collapseCascading();
-          return true;
-        });
-      }
-    });
-
-    selectFromSearchField(controller, foundName);
-    clearSearchField(controller);
-  }
-
-  Widget _buildSearchWidget(GlobalKey<State<StatefulWidget>> key) => Container(
-        width: wideSearchTextWidth,
-        height: defaultTextFieldHeight,
-        child: buildAutoCompleteSearchField(
-          controller: controller,
-          searchFieldKey: key,
-          searchFieldEnabled: !treeMapVisible,
-          shouldRequestFocus: !treeMapVisible,
-          onSelection: selectTheMatch,
-          supportClearField: true,
-        ),
-      );
-
-  Widget _buildSearchFilterControls() => Row(
-        mainAxisSize: MainAxisSize.min,
-        mainAxisAlignment: MainAxisAlignment.end,
-        children: [
-          _buildSearchWidget(memorySearchFieldKey),
-          const SizedBox(width: denseSpacing),
-          FilterButton(
-            key: MemoryScreenKeys.filterButton,
-            onPressed: _filter,
-            // TODO(kenz): implement isFilterActive
-            isFilterActive: false,
-          ),
-        ],
-      );
-
-  // TODO: Much of the logic for _takeHeapSnapshot() might want to move into the
-  // controller.
-  void _takeHeapSnapshot({bool userGenerated = true}) async {
-    // VmService not available (disconnected/crashed).
-    if (serviceManager.service == null) return;
-
-    // Another snapshot in progress, don't stall the world. An auto-snapshot
-    // is probably in progress.
-    if (snapshotState != SnapshotStatus.none &&
-        snapshotState != SnapshotStatus.done) {
-      debugLogger('Snapshot in progress - ignoring this request.');
-      return;
-    }
-
-    controller.memoryTimeline.addSnapshotEvent(auto: !userGenerated);
-
-    setState(() {
-      snapshotState = SnapshotStatus.streaming;
-    });
-
-    final snapshotTimestamp = DateTime.now();
-
-    final graph = await snapshotMemory();
-
-    // No snapshot collected, disconnected/crash application.
-    if (graph == null) {
-      setState(() {
-        snapshotState = SnapshotStatus.done;
-      });
-      controller.selectedSnapshotTimestamp = DateTime.now();
-      return;
-    }
-
-    final snapshotCollectionTime = DateTime.now();
-
-    setState(() {
-      snapshotState = SnapshotStatus.graphing;
-    });
-
-    // To debug particular classes add their names to the last
-    // parameter classNamesToMonitor e.g., ['AppStateModel', 'Terry', 'TerryEntry']
-    controller.heapGraph = convertHeapGraph(
-      controller.filterConfig,
-      graph,
-      [],
-    );
-    final snapshotGraphTime = DateTime.now();
-
-    setState(() {
-      snapshotState = SnapshotStatus.grouping;
-    });
-
-    await doGroupBy();
-
-    final root = controller.computeAllLibraries(graph: graph)!;
-
-    final snapshot = controller.storeSnapshot(
-      snapshotTimestamp,
-      graph,
-      root,
-      autoSnapshot: !userGenerated,
-    );
-
-    final snapshotDoneTime = DateTime.now();
-
-    controller.selectedSnapshotTimestamp = snapshotTimestamp;
-
-    debugLogger(
-      'Total Snapshot completed in'
-      ' ${snapshotDoneTime.difference(snapshotTimestamp).inMilliseconds / 1000} seconds',
-    );
-    debugLogger(
-      '  Snapshot collected in'
-      ' ${snapshotCollectionTime.difference(snapshotTimestamp).inMilliseconds / 1000} seconds',
-    );
-    debugLogger(
-      '  Snapshot graph built in'
-      ' ${snapshotGraphTime.difference(snapshotCollectionTime).inMilliseconds / 1000} seconds',
-    );
-    debugLogger(
-      '  Snapshot grouping/libraries computed in'
-      ' ${snapshotDoneTime.difference(snapshotGraphTime).inMilliseconds / 1000} seconds',
-    );
-
-    setState(() {
-      snapshotState = SnapshotStatus.done;
-    });
-
-    controller.buildTreeFromAllData();
-    _analyze(snapshot: snapshot);
-  }
-
-  Future<void> doGroupBy() async {
-    controller.heapGraph!
-      ..computeInstancesForClasses()
-      ..computeRawGroups()
-      ..computeFilteredGroups();
-  }
-
-  void _filter() {
-    // TODO(terry): Remove barrierDismissble and make clicking outside
-    //              dialog same as cancel.
-    // Dialog isn't dismissed by clicking outside the dialog (modal).
-    // Pressing either the Apply or Cancel button will dismiss.
-    unawaited(
-      showDialog(
-        context: context,
-        builder: (BuildContext context) => SnapshotFilterDialog(controller),
-        barrierDismissible: false,
-      ),
-    );
-  }
-
-  void _debugCheckAnalyses(DateTime currentSnapDateTime) {
-    // Debug only check.
-    assert(
-      () {
-        // Analysis already completed we're done.
-        final foundMatch = controller.completedAnalyses.firstWhereOrNull(
-          (element) => element.dateTime.compareTo(currentSnapDateTime) == 0,
-        );
-        if (foundMatch != null) {
-          logger.log(
-            'Analysis '
-            '${MemoryController.formattedTimestamp(currentSnapDateTime)} '
-            'already computed.',
-            logger.LogLevel.warning,
-          );
-        }
-        return true;
-      }(),
-    );
-  }
-
-  void _analyze({Snapshot? snapshot}) {
-    final AnalysesReference analysesNode = controller.findAnalysesNode()!;
-
-    snapshot ??= controller.computeSnapshotToAnalyze!;
-    final DateTime currentSnapDateTime = snapshot.collectedTimestamp;
-
-    _debugCheckAnalyses(currentSnapDateTime);
-
-    // If there's an empty place holder than remove it. First analysis will
-    // exist shortly.
-    if (analysesNode.children.length == 1 &&
-        analysesNode.children.first.name!.isEmpty) {
-      analysesNode.children.clear();
-    }
-
-    // Create analysis node to hold analysis results.
-    final analyzeSnapshot = AnalysisSnapshotReference(currentSnapDateTime);
-    analysesNode.addChild(analyzeSnapshot);
-
-    // Analyze this snapshot.
-    final collectedData = collect(controller, snapshot);
-
-    // Analyze the collected data.
-
-    // 1. Analysis of memory image usage.
-    imageAnalysis(controller, analyzeSnapshot, collectedData);
-
-    // Add to our list of completed analyses.
-    controller.completedAnalyses.add(analyzeSnapshot);
-
-    // Expand the 'Analysis' node.
-    if (!analysesNode.isExpanded) {
-      analysesNode.expand();
-    }
-
-    // Select the snapshot just analyzed.
-    controller.selectionSnapshotNotifier.value = Selection(
-      node: analyzeSnapshot,
-      nodeIndex: analyzeSnapshot.index,
-      scrollIntoView: true,
-    );
-  }
-}
-
-/// Snapshot TreeTable
-class MemoryHeapTable extends StatefulWidget {
-  @override
-  MemoryHeapTableState createState() => MemoryHeapTableState();
-}
-
-/// A table of the Memory graph class top-down call tree.
-class MemoryHeapTableState extends State<MemoryHeapTable>
-    with
-        AutoDisposeMixin,
-        ProvidedControllerMixin<MemoryController, MemoryHeapTable> {
-  final TreeColumnData<Reference> _treeColumn = _LibraryRefColumn();
-
-  final List<ColumnData<Reference>> _columns = [];
-
-  @override
-  void initState() {
-    super.initState();
-
-    // Setup the table columns.
-    _columns.addAll([
-      _treeColumn,
-      _ClassOrInstanceCountColumn(),
-      _ShallowSizeColumn(),
-      // TODO(terry): Don't display until dominator is implemented
-      //              Issue https://github.com/flutter/devtools/issues/2688
-      // _RetainedSizeColumn(),
-    ]);
-  }
-
-  @override
-  void didChangeDependencies() {
-    super.didChangeDependencies();
-    if (!initController()) return;
-
-    cancelListeners();
-
-    // Update the tree when the tree state changes e.g., expand, collapse, etc.
-    addAutoDisposeListener(controller.treeChangedNotifier, () {
-      if (controller.isTreeChanged) {
-        setState(() {});
-      }
-    });
-
-    // Update the tree when the memorySource changes.
-    addAutoDisposeListener(controller.selectedSnapshotNotifier, () {
-      setState(() {
-        controller.computeAllLibraries(rebuild: true);
-      });
-    });
-
-    addAutoDisposeListener(controller.filterNotifier, () {
-      setState(() {
-        controller.computeAllLibraries(rebuild: true);
-      });
-    });
-
-    addAutoDisposeListener(controller.searchAutoCompleteNotifier);
-
-    addAutoDisposeListener(controller.selectTheSearchNotifier, _handleSearch);
-
-    addAutoDisposeListener(controller.searchNotifier, _handleSearch);
-  }
-
-  void _handleSearch() {
-    final searchingValue = controller.search;
-    if (searchingValue.isNotEmpty) {
-      if (controller.selectTheSearch) {
-        // Found an exact match.
-        selectItemInTree(searchingValue);
-        controller.selectTheSearch = false;
-        controller.resetSearch();
-        return;
-      }
-
-      // No exact match, return the list of possible matches.
-      controller.clearSearchAutoComplete();
-
-      final matches = _snapshotMatches(searchingValue);
-
-      // Remove duplicates and sort the matches.
-      final normalizedMatches = matches.toSet().toList()..sort();
-      // Use the top 10 matches:
-      controller.searchAutoComplete.value = normalizedMatches
-          .sublist(
-            0,
-            min(
-              topMatchesLimit,
-              normalizedMatches.length,
-            ),
-          )
-          .map((match) => AutoCompleteMatch(match))
-          .toList();
-    }
-  }
-
-  List<String> _snapshotMatches(String searchingValue) {
-    final matches = <String>[];
-
-    final externalMatches = <String>[];
-    final filteredMatches = <String>[];
-
-    switch (controller.groupingBy.value) {
-      case MemoryController.groupByLibrary:
-        final searchRoot = controller.activeSnapshot;
-        for (final reference in searchRoot.children) {
-          if (reference.isLibrary) {
-            matches.addAll(
-              matchesInLibrary(reference as LibraryReference, searchingValue),
-            );
-          } else if (reference.isExternals) {
-            final refs = reference as ExternalReferences;
-            for (final ext in refs.children.cast<ExternalReference>()) {
-              final match = matchSearch(ext, searchingValue);
-              if (match != null) {
-                externalMatches.add(match);
-              }
-            }
-          } else if (reference.isFiltered) {
-            // Matches in the filtered nodes.
-            final filteredReference = reference as FilteredReference;
-            for (final library in filteredReference.children) {
-              filteredMatches.addAll(
-                matchesInLibrary(
-                  library as LibraryReference,
-                  searchingValue,
-                ),
-              );
-            }
-          }
-        }
-        break;
-      case MemoryController.groupByClass:
-        matches.addAll(
-          matchClasses(
-            controller.groupByTreeTable.dataRoots,
-            searchingValue,
-          ),
-        );
-        break;
-      case MemoryController.groupByInstance:
-        // TODO(terry): TBD
-        break;
-    }
-
-    // Ordered in importance (matches, external, filtered).
-    matches.addAll(externalMatches);
-    matches.addAll(filteredMatches);
-    return matches;
-  }
-
-  List<String> _maybeAddMatch(Reference reference, String search) {
-    final matches = <String>[];
-
-    final match = matchSearch(reference, search);
-    if (match != null) {
-      matches.add(match);
-    }
-
-    return matches;
-  }
-
-  List<String> matchesInLibrary(
-    LibraryReference libraryReference,
-    String searchingValue,
-  ) {
-    final matches = _maybeAddMatch(libraryReference, searchingValue);
-
-    final List<Reference> classes = libraryReference.children;
-    matches.addAll(matchClasses(classes, searchingValue));
-
-    return matches;
-  }
-
-  List<String> matchClasses(
-    List<Reference> classReferences,
-    String searchingValue,
-  ) {
-    final matches = <String>[];
-
-    // Check the class names in the library
-    for (final ClassReference classReference
-        in classReferences.cast<ClassReference>()) {
-      matches.addAll(_maybeAddMatch(classReference, searchingValue));
-    }
-
-    // Remove duplicates
-    return matches;
-  }
-
-  /// Return null if no match, otherwise string.
-  String? matchSearch(Reference ref, String matchString) {
-    final knownName = ref.name!.toLowerCase();
-    if (knownName.contains(matchString.toLowerCase())) {
-      return ref.name;
-    }
-    return null;
-  }
-
-  /// This finds and selects an exact match in the tree.
-  /// Returns `true` if [searchingValue] is found in the tree.
-  bool selectItemInTree(String searchingValue) {
-    // Search the snapshots.
-    switch (controller.groupingBy.value) {
-      case MemoryController.groupByLibrary:
-        final searchRoot = controller.activeSnapshot;
-        if (controller.selectionSnapshotNotifier.value.node == null) {
-          // No selected node, then select the snapshot we're searching.
-          controller.selectionSnapshotNotifier.value = Selection(
-            node: searchRoot,
-            nodeIndex: searchRoot.index,
-            scrollIntoView: true,
-          );
-        }
-        for (final reference in searchRoot.children) {
-          if (reference.isLibrary) {
-            final foundIt = _selectItemInTree(reference, searchingValue);
-            if (foundIt) {
-              return true;
-            }
-          } else if (reference.isFiltered) {
-            // Matches in the filtered nodes.
-            final filteredReference = reference as FilteredReference;
-            for (final library in filteredReference.children) {
-              final foundIt = _selectItemInTree(library, searchingValue);
-              if (foundIt) {
-                return true;
-              }
-            }
-          } else if (reference.isExternals) {
-            final refs = reference as ExternalReferences;
-            for (final external in refs.children.cast<ExternalReference>()) {
-              final foundIt = _selectItemInTree(external, searchingValue);
-              if (foundIt) {
-                return true;
-              }
-            }
-          }
-        }
-        break;
-      case MemoryController.groupByClass:
-        for (final reference in controller.groupByTreeTable.dataRoots) {
-          if (reference.isClass) {
-            return _selecteClassInTree(reference, searchingValue);
-          }
-        }
-        break;
-      case MemoryController.groupByInstance:
-        // TODO(terry): TBD
-        break;
-    }
-
-    return false;
-  }
-
-  bool _selectInTree(Reference reference, search) {
-    if (reference.name == search) {
-      controller.selectionSnapshotNotifier.value = Selection(
-        node: reference,
-        nodeIndex: reference.index,
-        scrollIntoView: true,
-      );
-      controller.clearSearchAutoComplete();
-      return true;
-    }
-    return false;
-  }
-
-  bool _selectItemInTree(Reference reference, String searchingValue) {
-    // TODO(terry): Only finds first one.
-    if (_selectInTree(reference, searchingValue)) return true;
-
-    // Check the class names in the library
-    return _selecteClassInTree(reference, searchingValue);
-  }
-
-  bool _selecteClassInTree(Reference reference, String searchingValue) {
-    // Check the class names in the library
-    for (final Reference classReference in reference.children) {
-      // TODO(terry): Only finds first one.
-      if (_selectInTree(classReference, searchingValue)) return true;
-    }
-
-    return false;
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    final root = controller.buildTreeFromAllData();
-    Widget result;
-    if (root != null && root.children.isNotEmpty) {
-      // Snapshots and analyses exists display the trees.
-      controller.groupByTreeTable = TreeTable<Reference>(
-        keyFactory: (libRef) => PageStorageKey<String?>(libRef.name),
-        dataRoots: root.children,
-        dataKey: 'memory-snapshot-tree',
-        columns: _columns,
-        treeColumn: _treeColumn,
-        defaultSortColumn: _columns[0],
-        defaultSortDirection: SortDirection.ascending,
-        selectionNotifier: controller.selectionSnapshotNotifier,
-      );
-
-      result = controller.groupByTreeTable;
-    } else {
-      // Nothing collected yet (snapshots/analyses) - return an empty area.
-      result = const SizedBox();
-    }
-    return OutlineDecoration(child: result);
-  }
-}
-
-class _LibraryRefColumn extends TreeColumnData<Reference> {
-  _LibraryRefColumn() : super('Library or Class');
-
-  @override
-  String getValue(Reference dataObject) {
-    // Should never be empty when we're displaying in table.
-    assert(!dataObject.isEmptyReference);
-
-    if (dataObject.isLibrary) {
-      final value = (dataObject as LibraryReference).name ?? '';
-      final splitValues = value.split('/');
-
-      return splitValues.length > 1
-          ? '${splitValues[0]}/${splitValues[1]}'
-          : splitValues[0];
-    }
-
-    return dataObject.name ?? '';
-  }
-
-  @override
-  String getDisplayValue(Reference dataObject) => getValue(dataObject);
-
-  @override
-  bool get supportsSorting => true;
-
-  @override
-  String getTooltip(Reference dataObject) {
-    return dataObject.name ?? '';
-  }
-}
-
-class _ClassOrInstanceCountColumn extends ColumnData<Reference> {
-  _ClassOrInstanceCountColumn()
-      : super(
-          'Count',
-          alignment: ColumnAlignment.right,
-          fixedWidthPx: scaleByFontFactor(75.0),
-        );
-
-  @override
-  int getValue(Reference dataObject) {
-    assert(!dataObject.isEmptyReference);
-
-    // Although the method returns dynamic this implementatation only
-    // returns int.
-
-    if (dataObject.name == MemoryController.libraryRootNode ||
-        dataObject.name == MemoryController.classRootNode) return 0;
-
-    if (dataObject.isExternal) {
-      return dataObject.children.length;
-    }
-
-    if (dataObject.isAnalysis && dataObject is AnalysisReference) {
-      final AnalysisReference analysisReference = dataObject;
-      return analysisReference.countNote ?? 0;
-    }
-
-    if (dataObject.isSnapshot && dataObject is SnapshotReference) {
-      int count = 0;
-      final SnapshotReference snapshotRef = dataObject;
-      for (final child in snapshotRef.children) {
-        count += _computeCount(child);
-      }
-      return count;
-    }
-
-    return _computeCount(dataObject);
-  }
-
-  /// Return a count based on the Reference type e.g., library, filtered,
-  /// class, externals, etc.  Only compute once, store in the Reference.
-  int _computeCount(Reference ref) {
-    // Only compute the children counts once then store in the Reference.
-    if (ref.hasCount) return ref.count!;
-
-    int count = 0;
-
-    if (ref.isClass) {
-      final classRef = ref as ClassReference;
-      count = _computeClassInstances(classRef.actualClass!)!;
-    } else if (ref.isLibrary) {
-      // Return number of classes.
-      final LibraryReference libraryReference = ref as LibraryReference;
-      for (final heapClass in libraryReference.actualClasses!) {
-        count += _computeClassInstances(heapClass)!;
-      }
-    } else if (ref.isFiltered) {
-      final FilteredReference filteredRef = ref as FilteredReference;
-      for (final LibraryReference child
-          in filteredRef.children.cast<LibraryReference>()) {
-        for (final heapClass in child.actualClasses!) {
-          count += _computeClassInstances(heapClass)!;
-        }
-      }
-    } else if (ref.isExternals) {
-      for (ExternalReference externalRef
-          in ref.children.cast<ExternalReference>()) {
-        count += externalRef.children.length;
-      }
-    } else if (ref.isExternal) {
-      final ExternalReference externalRef = ref as ExternalReference;
-      count = externalRef.children.length;
-    }
-
-    // Only compute once.
-    ref.count = count;
-
-    return count;
-  }
-
-  int? _computeClassInstances(HeapGraphClassLive liveClass) =>
-      liveClass.instancesCount != null ? liveClass.instancesCount : null;
-
-  /// Internal helper for all count values.
-  String _displayCount(int? count) => count == null ? '--' : '$count';
-
-  @override
-  String getDisplayValue(Reference dataObject) =>
-      _displayCount(getValue(dataObject));
-
-  @override
-  bool get supportsSorting => true;
-
-  @override
-  int compare(Reference a, Reference b) {
-    // Analysis is always before.
-    final Comparable valueA = a.isAnalysis ? 0 : getValue(a);
-    final Comparable valueB = b.isAnalysis ? 0 : getValue(b);
-    return valueA.compareTo(valueB);
-  }
-}
-
-class _ShallowSizeColumn extends ColumnData<Reference> {
-  _ShallowSizeColumn()
-      : super(
-          'Shallow',
-          alignment: ColumnAlignment.right,
-          fixedWidthPx: scaleByFontFactor(100.0),
-        );
-
-  int sizeAllVisibleLibraries(List<Reference> references) {
-    var sum = 0;
-    for (final ref in references) {
-      if (ref.isLibrary) {
-        final libraryReference = ref as LibraryReference;
-        for (final actualClass in libraryReference.actualClasses!) {
-          sum += actualClass.instancesTotalShallowSizes;
-        }
-      }
-    }
-    return sum;
-  }
-
-  @override
-  dynamic getValue(Reference dataObject) {
-    // Should never be empty when we're displaying in table.
-    assert(!dataObject.isEmptyReference);
-
-    if (dataObject.name == MemoryController.libraryRootNode ||
-        dataObject.name == MemoryController.classRootNode) {
-      final snapshot = dataObject.controller!.getSnapshot(dataObject)!;
-      final snapshotGraph = snapshot.snapshotGraph;
-      return snapshotGraph.shallowSize + snapshotGraph.externalSize;
-    }
-
-    if (dataObject.isAnalysis && dataObject is AnalysisReference) {
-      final AnalysisReference analysisReference = dataObject;
-      final size = analysisReference.sizeNote;
-      return size ?? '';
-    } else if (dataObject.isSnapshot && dataObject is SnapshotReference) {
-      var sum = 0;
-      final SnapshotReference snapshotRef = dataObject;
-      for (final childRef in snapshotRef.children) {
-        sum += _sumShallowSize(childRef) ?? 0;
-      }
-      return sum;
-    } else {
-      final sum = _sumShallowSize(dataObject);
-      return sum ?? '';
-    }
-  }
-
-  int? _sumShallowSize(Reference ref) {
-    if (ref.isLibrary) {
-      // Return number of classes.
-      final libraryReference = ref as LibraryReference;
-      var sum = 0;
-      for (final actualClass in libraryReference.actualClasses!) {
-        sum += actualClass.instancesTotalShallowSizes;
-      }
-      return sum;
-    } else if (ref.isClass) {
-      final classReference = ref as ClassReference;
-      return classReference.actualClass!.instancesTotalShallowSizes;
-    } else if (ref.isObject) {
-      // Return number of instances.
-      final objectReference = ref as ObjectReference;
-
-      var size = objectReference.instance.origin.shallowSize;
-
-      // If it's an external object then return the externalSize too.
-      if (ref is ExternalObjectReference) {
-        final ExternalObjectReference externalRef = ref;
-        size += externalRef.externalSize;
-      }
-
-      return size;
-    } else if (ref.isFiltered) {
-      final snapshot = ref.controller!.getSnapshot(ref)!;
-      final sum = sizeAllVisibleLibraries(snapshot.libraryRoot?.children ?? []);
-      final snapshotGraph = snapshot.snapshotGraph;
-      return snapshotGraph.shallowSize - sum;
-    } else if (ref.isExternals) {
-      final snapshot = ref.controller!.getSnapshot(ref)!;
-      return snapshot.snapshotGraph.externalSize;
-    } else if (ref.isExternal) {
-      return (ref as ExternalReference).sumExternalSizes;
-    }
-
-    return null;
-  }
-
-  @override
-  String getDisplayValue(Reference dataObject) {
-    final value = getValue(dataObject);
-
-    // TODO(terry): Add percent to display too.
-/*
-    final total = dataObject.controller.snapshots.last.snapshotGraph.capacity;
-    final percentage = (value / total) * 100;
-    final displayPercentage = percentage < .050 ? '<<1%'
-        : '${NumberFormat.compact().format(percentage)}%';
-    print('$displayPercentage [${NumberFormat.compact().format(percentage)}%]');
-*/
-    if ((dataObject.isAnalysis ||
-            dataObject.isAllocations ||
-            dataObject.isAllocation) &&
-        value is! int) return '';
-
-    return prettyPrintBytes(
-      value as int,
-      kbFractionDigits: 1,
-      includeUnit: true,
-    )!;
-  }
-
-  @override
-  bool get supportsSorting => true;
-
-  @override
-  int compare(Reference a, Reference b) {
-    // Analysis is always before.
-    final Comparable valueA = a.isAnalysis ? 0 : getValue(a);
-    final Comparable valueB = b.isAnalysis ? 0 : getValue(b);
-    return valueA.compareTo(valueB);
-  }
-}
-
-// TODO(terry): Remove ignore when dominator is implemented.
-// ignore: unused_element
-class _RetainedSizeColumn extends ColumnData<Reference> {
-  _RetainedSizeColumn()
-      : super(
-          'Retained',
-          alignment: ColumnAlignment.right,
-          fixedWidthPx: scaleByFontFactor(100.0),
-        );
-
-  @override
-  dynamic getValue(Reference dataObject) {
-    // Should never be empty when we're displaying in table.
-    assert(!dataObject.isEmptyReference);
-
-    if (dataObject.name == MemoryController.libraryRootNode ||
-        dataObject.name == MemoryController.classRootNode) return '';
-
-    if (dataObject.isLibrary) {
-      // Return number of classes.
-      final libraryReference = dataObject as LibraryReference;
-      var sum = 0;
-      for (final actualClass in libraryReference.actualClasses!) {
-        sum += actualClass.instancesTotalShallowSizes;
-      }
-      return sum;
-    } else if (dataObject.isClass) {
-      final classReference = dataObject as ClassReference;
-      return classReference.actualClass!.instancesTotalShallowSizes;
-    } else if (dataObject.isObject) {
-      // Return number of instances.
-      final objectReference = dataObject as ObjectReference;
-      return objectReference.instance.origin.shallowSize;
-    } else if (dataObject.isExternals) {
-      return dataObject.controller!.lastSnapshot!.snapshotGraph.externalSize;
-    } else if (dataObject.isExternal) {
-      return (dataObject as ExternalReference)
-          .liveExternal
-          .externalProperty
-          .externalSize;
-    }
-
-    return '';
-  }
-
-  @override
-  String getDisplayValue(Reference dataObject) {
-    final value = getValue(dataObject);
-    return '$value';
-  }
-
-  @override
-  bool get supportsSorting => true;
-
-  @override
-  int compare(Reference a, Reference b) {
-    // Analysis is always before.
-    final Comparable valueA = a.isAnalysis ? 0 : getValue(a);
-    final Comparable valueB = b.isAnalysis ? 0 : getValue(b);
-    return valueA.compareTo(valueB);
-  }
-}
diff --git a/packages/devtools_app/lib/src/screens/memory/memory_heap_treemap.dart b/packages/devtools_app/lib/src/screens/memory/memory_heap_treemap.dart
deleted file mode 100644
index 88517d7..0000000
--- a/packages/devtools_app/lib/src/screens/memory/memory_heap_treemap.dart
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:flutter/material.dart' hide TextStyle;
-import 'package:flutter/widgets.dart' hide TextStyle;
-
-import '../../charts/treemap.dart';
-import '../../primitives/auto_dispose_mixin.dart';
-import '../../shared/common_widgets.dart';
-import '../../shared/theme.dart';
-import '../../shared/utils.dart';
-import 'memory_controller.dart';
-import 'primitives/predefined_classes.dart';
-
-class MemoryHeapTreemap extends StatefulWidget {
-  const MemoryHeapTreemap();
-
-  @override
-  MemoryHeapTreemapState createState() => MemoryHeapTreemapState();
-}
-
-class MemoryHeapTreemapState extends State<MemoryHeapTreemap>
-    with
-        AutoDisposeMixin,
-        ProvidedControllerMixin<MemoryController, MemoryHeapTreemap> {
-  InstructionsSize? _sizes;
-
-  TreemapNode? root;
-
-  @override
-  void didChangeDependencies() {
-    super.didChangeDependencies();
-
-    // TODO(terry): Unable to short-circuit need to investigate why?
-    if (!initController()) return;
-
-    if (controller.heapGraph != null) {
-      _sizes = InstructionsSize.fromSnapshot(controller);
-      root = _sizes!.root;
-    }
-
-    cancelListeners();
-
-    addAutoDisposeListener(controller.selectedSnapshotNotifier, () {
-      setState(() {
-        controller.computeAllLibraries(rebuild: true);
-
-        _sizes = InstructionsSize.fromSnapshot(controller);
-      });
-    });
-
-    // TODO(peterdjlee): Implement search and filter functionality for memory treemap.
-    // addAutoDisposeListener(controller.filterNotifier, () {
-    //   setState(() {
-    //     controller.computeAllLibraries(rebuild: true);
-    //   });
-    // });
-    // addAutoDisposeListener(controller.selectTheSearchNotifier, () {
-    //   setState(() {
-    //     if (_trySelectItem()) {
-    //       closeAutoCompleteOverlay();
-    //     }
-    //   });
-    // });
-
-    // addAutoDisposeListener(controller.searchNotifier, () {
-    //   setState(() {
-    //     if (_trySelectItem()) {
-    //       closeAutoCompleteOverlay();
-    //     }
-    //   });
-    // });
-
-    // addAutoDisposeListener(controller.searchAutoCompleteNotifier, () {
-    //   setState(autoCompleteOverlaySetState(controller, context));
-    // });
-  }
-
-  void _onRootChanged(TreemapNode? newRoot) {
-    setState(() {
-      root = newRoot;
-    });
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    Widget result;
-    if (_sizes == null) {
-      result = Column(
-        children: [
-          const SizedBox(height: denseRowSpacing),
-          Expanded(
-            child: OutlineDecoration(
-              child: Row(children: const [SizedBox()]),
-            ),
-          ),
-        ],
-      );
-    } else {
-      result = Padding(
-        padding: const EdgeInsets.only(top: denseRowSpacing),
-        child: LayoutBuilder(
-          builder: (context, constraints) {
-            return Treemap.fromRoot(
-              rootNode: root,
-              levelsVisible: 2,
-              isOutermostLevel: true,
-              width: constraints.maxWidth,
-              height: constraints.maxHeight,
-              onRootChangedCallback: _onRootChanged,
-            );
-          },
-        ),
-      );
-    }
-    return OutlineDecoration(child: result);
-  }
-}
-
-/// Definitions of exposed callback methods stored in callback Map the key
-/// is the function name (String) and the value a callback function signature.
-
-/// matchNames callback name.
-const matchNamesKey = 'matchNames';
-
-/// matchNames callback signature.
-typedef MatchNamesFunction = List<String> Function(String);
-
-/// findNode callback name.
-const findNodeKey = 'findNode';
-
-/// findNode callback signature.
-typedef FindNodeFunction = TreemapNode Function(String);
-
-/// selectNode callback name.
-const selectNodeKey = 'selectNode';
-
-/// selectNode callback signature.
-typedef SelectNodeFunction = void Function(TreemapNode);
-
-class InstructionsSize {
-  const InstructionsSize(this.root);
-
-  factory InstructionsSize.fromSnapshot(MemoryController controller) {
-    final rootChildren = <String, TreemapNode>{};
-    final root = TreemapNode(
-      name: 'root',
-      childrenMap: rootChildren,
-    );
-    TreemapNode? currentParent = root;
-
-    // TODO(terry): Should treemap be all memory or just the filtered group?
-    //              Using rawGroup not graph.groupByLibrary.
-
-    (controller.heapGraph?.rawGroupByLibrary ?? {}).forEach(
-      (libraryGroup, value) {
-        final classes = value;
-        for (final theClass in classes) {
-          final shallowSize = theClass.instancesTotalShallowSizes;
-          var className = theClass.name;
-          if (shallowSize == 0 || className == '::') {
-            continue;
-          }
-
-          // Ensure the empty library name is our group name e.g., '' -> 'src'.
-          String libraryName = theClass.libraryUri.toString();
-          if (libraryName.isEmpty) {
-            libraryName = libraryGroup;
-          }
-
-          // Map class names to familar user names.
-          final predefined =
-              predefinedClasses[LibraryClass(libraryName, className)];
-          if (predefined != null) {
-            className = predefined.prettyName;
-          }
-
-          final symbol = Symbol(
-            name: 'new $className',
-            size: shallowSize,
-            libraryUri: libraryName,
-            className: className,
-          );
-
-          Map<String, TreemapNode> currentChildren = rootChildren;
-          final parentReset = currentParent;
-          for (String pathPart in symbol.parts) {
-            currentChildren.putIfAbsent(
-              pathPart,
-              () {
-                final node = TreemapNode(
-                  name: pathPart,
-                  childrenMap: <String, TreemapNode>{},
-                );
-                currentParent!.addChild(node);
-                return node;
-              },
-            );
-            currentChildren[pathPart]!.byteSize += symbol.size;
-            currentParent = currentChildren[pathPart];
-            currentChildren = currentChildren[pathPart]!.childrenMap;
-          }
-          currentParent = parentReset;
-        }
-      },
-    );
-
-    // Get sum of children's sizes.
-    root.byteSize = root.childrenMap.values
-        .fold(0, (int current, TreemapNode node) => current + node.byteSize);
-
-    final snapshotGraph = controller.snapshots.last.snapshotGraph;
-    // Add the external heap to the treemap.
-    root.childrenMap.putIfAbsent('External Heap', () {
-      final node = TreemapNode(
-        name: 'External Heap',
-        childrenMap: <String, TreemapNode>{},
-      )..byteSize = snapshotGraph.externalSize;
-      root.addChild(node);
-      return node;
-    });
-
-    // TODO(peterdjlee): Add the filtered libraries/classes to the treemap.
-
-    root.byteSize = snapshotGraph.shallowSize + snapshotGraph.externalSize;
-
-    return InstructionsSize(root);
-  }
-
-  final TreemapNode root;
-}
-
-class Symbol {
-  const Symbol({
-    required this.name,
-    required this.size,
-    this.libraryUri,
-    this.className,
-  });
-
-  static Symbol fromMap(Map<String, dynamic> json) {
-    return Symbol(
-      name: json['n'] as String,
-      size: json['s'] as int,
-      className: json['c'] as String?,
-      libraryUri: json['l'] as String?,
-    );
-  }
-
-  final String name;
-  final int size;
-  final String? libraryUri;
-  final String? className;
-
-  List<String> get parts {
-    return <String>[
-      if (libraryUri != null) ...libraryUri!.split('/') else '@stubs',
-      if (className?.isNotEmpty ?? false) className!,
-      name,
-    ];
-  }
-}
diff --git a/packages/devtools_app/lib/src/screens/memory/memory_instance_tree_view.dart b/packages/devtools_app/lib/src/screens/memory/memory_instance_tree_view.dart
deleted file mode 100644
index 44493dc..0000000
--- a/packages/devtools_app/lib/src/screens/memory/memory_instance_tree_view.dart
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:flutter/material.dart';
-
-import '../../primitives/auto_dispose_mixin.dart';
-import '../../primitives/utils.dart';
-import '../../shared/table/table.dart';
-import '../../shared/table/table_data.dart';
-import '../../shared/utils.dart';
-import 'memory_controller.dart';
-import 'memory_snapshot_models.dart';
-
-class InstanceTreeView extends StatefulWidget {
-  @override
-  InstanceTreeViewState createState() => InstanceTreeViewState();
-}
-
-/// Table of the fields of an instance (type, name and value).
-class InstanceTreeViewState extends State<InstanceTreeView>
-    with
-        AutoDisposeMixin,
-        ProvidedControllerMixin<MemoryController, InstanceTreeView> {
-  final TreeColumnData<FieldReference> treeColumn = _FieldTypeColumn();
-
-  final List<ColumnData<FieldReference>> columns = [];
-
-  @override
-  void initState() {
-    super.initState();
-
-    // Setup table column names for instance viewer.
-    columns.addAll([
-      treeColumn,
-      _FieldNameColumn(),
-      _FieldValueColumn(),
-    ]);
-  }
-
-  @override
-  void didChangeDependencies() {
-    super.didChangeDependencies();
-    if (!initController()) return;
-
-    cancelListeners();
-
-    // TODO(terry): setState should be called to set our state not change the
-    //              controller. Have other ValueListenables on controller to
-    //              listen to, so we don't need the setState calls.
-    // Update the chart when the memorySource changes.
-    addAutoDisposeListener(controller.selectedSnapshotNotifier, () {
-      setState(() {
-        controller.computeAllLibraries(rebuild: true);
-      });
-    });
-  }
-
-  @override
-  Widget build(BuildContext context) {
-    controller.instanceFieldsTreeTable = TreeTable<FieldReference>(
-      keyFactory: (typeRef) => PageStorageKey<String>(typeRef.name),
-      dataRoots: controller.instanceRoot!,
-      dataKey: 'memory-instance-tree',
-      columns: columns,
-      treeColumn: treeColumn,
-      defaultSortColumn: columns[0],
-      defaultSortDirection: SortDirection.ascending,
-    );
-
-    return controller.instanceFieldsTreeTable!;
-  }
-}
-
-class _FieldTypeColumn extends TreeColumnData<FieldReference> {
-  _FieldTypeColumn() : super('Type');
-
-  @override
-  String getValue(FieldReference dataObject) =>
-      dataObject.isEmptyReference || dataObject.isSentinelReference
-          ? ''
-          : dataObject.type ?? '';
-
-  @override
-  String getDisplayValue(FieldReference dataObject) =>
-      '${getValue(dataObject)}';
-
-  @override
-  bool get supportsSorting => true;
-
-  @override
-  int compare(FieldReference a, FieldReference b) {
-    final valueA = getValue(a);
-    final valueB = getValue(b);
-    return valueA.compareTo(valueB);
-  }
-
-  @override
-  double get fixedWidthPx => 250.0;
-}
-
-class _FieldNameColumn extends ColumnData<FieldReference> {
-  _FieldNameColumn()
-      : super(
-          'Name',
-          fixedWidthPx: scaleByFontFactor(150.0),
-        );
-
-  @override
-  String getValue(FieldReference dataObject) =>
-      dataObject.isEmptyReference ? '' : dataObject.name;
-
-  @override
-  String getDisplayValue(FieldReference dataObject) =>
-      '${getValue(dataObject)}';
-
-  @override
-  bool get supportsSorting => true;
-
-  @override
-  int compare(FieldReference a, FieldReference b) {
-    final valueA = getValue(a);
-    final valueB = getValue(b);
-    return valueA.compareTo(valueB);
-  }
-}
-
-class _FieldValueColumn extends ColumnData<FieldReference> {
-  _FieldValueColumn()
-      : super(
-          'Value',
-          fixedWidthPx: scaleByFontFactor(350.0),
-        );
-
-  @override
-  String getValue(FieldReference dataObject) =>
-      dataObject.isEmptyReference || dataObject.isSentinelReference
-          ? ''
-          : dataObject.value ?? '';
-
-  @override
-  String getDisplayValue(FieldReference dataObject) {
-    if (dataObject is ObjectFieldReference && !dataObject.isNull) {
-      // Real object that isn't Null value is empty string.
-      return '';
-    }
-
-    // TODO(terry): Can we use standard Flutter string truncation?
-    //              Long variable names the beginning and end of the name is
-    //              most significant the middle part of name less so.  However,
-    //              we need a nice hover/tooltip to show the entire name.
-    //              If middle, '...' then we need a more robust computation w/
-    //              text measurement so we accurately truncate the correct # of
-    //              characters instead guessing that we only show 30 chars.
-    var value = getValue(dataObject);
-    if (value.length > 30) {
-      value = '${value.substring(0, 13)}…${value.substring(value.length - 17)}';
-    }
-    return '$value';
-  }
-
-  @override
-  bool get supportsSorting => true;
-
-  @override
-  int compare(FieldReference a, FieldReference b) {
-    final valueA = getValue(a);
-    final valueB = getValue(b);
-    return valueA.compareTo(valueB);
-  }
-}
diff --git a/packages/devtools_app/lib/src/screens/memory/memory_screen.dart b/packages/devtools_app/lib/src/screens/memory/memory_screen.dart
index 3cb313c..589b8cf 100644
--- a/packages/devtools_app/lib/src/screens/memory/memory_screen.dart
+++ b/packages/devtools_app/lib/src/screens/memory/memory_screen.dart
@@ -14,7 +14,7 @@
 import '../../shared/utils.dart';
 import '../../ui/icons.dart';
 import 'memory_controller.dart';
-import 'memory_heap_tree_view.dart';
+import 'memory_tabs.dart';
 import 'panes/chart/chart_pane.dart';
 import 'panes/chart/chart_pane_controller.dart';
 import 'panes/chart/memory_android_chart.dart';
@@ -84,16 +84,6 @@
         sharedLabels: vmChartController.labelTimestamps,
       ),
     );
-
-    // Update the chart when the memorySource changes.
-    addAutoDisposeListener(memoryController.selectedSnapshotNotifier, () {
-      setState(() {
-        // TODO(terry): Create the snapshot data to display by Library,
-        //              by Class or by Objects.
-        // Create the snapshot data by Library.
-        memoryController.createSnapshotByLibrary();
-      });
-    });
   }
 
   @override
@@ -111,7 +101,7 @@
           keyFocusNode: _focusNode,
         ),
         Expanded(
-          child: HeapTreeView(memoryController),
+          child: MemoryTabs(memoryController),
         ),
       ],
     );
diff --git a/packages/devtools_app/lib/src/screens/memory/memory_snapshot_models.dart b/packages/devtools_app/lib/src/screens/memory/memory_snapshot_models.dart
deleted file mode 100644
index 638e15f..0000000
--- a/packages/devtools_app/lib/src/screens/memory/memory_snapshot_models.dart
+++ /dev/null
@@ -1,1085 +0,0 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:collection/collection.dart';
-import 'package:vm_service/vm_service.dart';
-
-import '../../primitives/trees.dart';
-import 'memory_controller.dart';
-import 'memory_graph_model.dart';
-import 'primitives/predefined_classes.dart';
-
-/// Consolidated list of libraries.  External is the external heap
-/// and Filtered is the sum of all filtered (hidden) libraries.
-const externalLibraryName = 'External';
-const filteredLibrariesName = 'Filtered';
-
-/// Name for predefined Reference and FieldReference should never be
-/// seen by user.
-const emptyName = '<empty>';
-const sentinelName = '<sentinel>';
-
-class Reference extends TreeNode<Reference> {
-  factory Reference({
-    MemoryController? argController,
-    HeapGraphClassLive? argActualClass,
-    String? argName,
-    bool argIsAllocation = false,
-    bool argIsAllocations = false,
-    bool argIsAnalysis = false,
-    bool argIsSnapshot = false,
-    bool argIsLibrary = false,
-    bool argIsExternals = false,
-    bool argIsExternal = false,
-    bool argIsFiltered = false,
-    bool argIsClass = false,
-    bool argIsObject = false,
-    Function? argOnExpand,
-    Function? argOnLeaf,
-  }) {
-    return Reference._internal(
-      controller: argController,
-      actualClass: argActualClass,
-      name: argName,
-      isAllocation: argIsAllocation,
-      isAllocations: argIsAllocations,
-      isAnalysis: argIsAnalysis,
-      isSnapshot: argIsSnapshot,
-      isLibrary: argIsLibrary,
-      isExternals: argIsExternals,
-      isFiltered: argIsFiltered,
-      isClass: argIsClass,
-      isObject: argIsObject,
-      onExpand: argOnExpand,
-      onLeaf: argOnLeaf,
-    );
-  }
-
-  Reference._internal({
-    this.controller,
-    this.actualClass,
-    this.name,
-    this.isAllocation = false,
-    this.isAllocations = false,
-    this.isAnalysis = false,
-    this.isSnapshot = false,
-    this.isLibrary = false,
-    this.isExternals = false,
-    this.isExternal = false,
-    this.isFiltered = false,
-    this.isClass = false,
-    this.isObject = false,
-    this.onExpand,
-    this.onLeaf,
-  });
-
-  factory Reference._empty() => Reference._internal(name: emptyName);
-
-  factory Reference._sentinel() => Reference._internal(name: sentinelName);
-
-  Reference.allocationsMonitor(
-    String name, {
-    Function? onExpand,
-    Function? onLeaf,
-  }) : this._internal(
-          controller: null,
-          name: name,
-          isAllocations: true,
-          onExpand: onExpand,
-          onLeaf: onLeaf,
-        );
-
-  Reference.allocationMonitor(
-    MemoryController controller,
-    String name, {
-    Function? onExpand,
-    Function? onLeaf,
-  }) : this._internal(
-          controller: controller,
-          name: name,
-          isAllocation: true,
-          onExpand: onExpand,
-          onLeaf: onLeaf,
-        );
-
-  Reference.analysis(
-    MemoryController? controller,
-    String name, {
-    Function? onExpand,
-    Function? onLeaf,
-  }) : this._internal(
-          controller: controller,
-          name: name,
-          isAnalysis: true,
-          onExpand: onExpand,
-          onLeaf: onLeaf,
-        );
-
-  Reference.snapshot(
-    MemoryController? controller,
-    String name, {
-    Function? onExpand,
-    Function? onLeaf,
-  }) : this._internal(
-          controller: controller,
-          name: name,
-          isSnapshot: true,
-          onExpand: onExpand,
-          onLeaf: onLeaf,
-        );
-
-  Reference.library(
-    MemoryController controller,
-    String name, {
-    Function? onExpand,
-    Function? onLeaf,
-  }) : this._internal(
-          controller: controller,
-          name: name,
-          isLibrary: true,
-          onExpand: onExpand,
-          onLeaf: onLeaf,
-        );
-
-  Reference.aClass(
-    MemoryController controller,
-    HeapGraphClassLive actualClass, {
-    Function? onExpand,
-    Function? onLeaf,
-  }) : this._internal(
-          controller: controller,
-          actualClass: actualClass,
-          name: actualClass.name,
-          isClass: true,
-          onExpand: onExpand,
-          onLeaf: onLeaf,
-        );
-
-  /// Name is the object name.
-  Reference.object(
-    MemoryController? controller,
-    String name, {
-    Function? onExpand,
-    Function? onLeaf,
-  }) : this._internal(
-          controller: controller,
-          name: name,
-          isObject: true,
-          onExpand: onExpand,
-          onLeaf: onLeaf,
-        );
-
-  /// External heap
-  Reference.externals(
-    MemoryController controller, {
-    Function? onExpand,
-  }) : this._internal(
-          controller: controller,
-          name: externalLibraryName,
-          isExternals: true,
-          onExpand: onExpand,
-        );
-
-  /// External objects
-  Reference.external(
-    MemoryController controller,
-    String name, {
-    Function? onExpand,
-  }) : this._internal(
-          controller: controller,
-          name: name,
-          isExternal: true,
-          onExpand: onExpand,
-        );
-
-  /// All filtered libraries and classes
-  Reference.filtered(MemoryController controller)
-      : this._internal(
-          controller: controller,
-          name: filteredLibrariesName,
-          isFiltered: true,
-        );
-
-  static Reference empty = Reference._empty();
-
-  bool get isEmptyReference => this == empty;
-
-  static Reference sentinel = Reference._sentinel();
-
-  bool get isSentinelReference => this == sentinel;
-
-  final MemoryController? controller;
-
-  final HeapGraphClassLive? actualClass;
-
-  final String? name;
-
-  /// Allocations are being monitored, user requested monitoring of any
-  /// allocated memory.  Parent node containing all monitored classes.
-  final bool isAllocations;
-
-  /// A monitored class.
-  final bool isAllocation;
-
-  final bool isAnalysis;
-
-  final bool isSnapshot;
-
-  final bool isLibrary;
-
-  /// The External heap (contains all external heap items).
-  final bool isExternals;
-
-  /// An External heap item.
-  final bool isExternal;
-
-  /// Class or Library is filtered.
-  final bool isFiltered;
-
-  final bool isClass;
-
-  final bool isObject;
-
-  int? count;
-
-  bool get hasCount => count != null;
-
-  Function? onExpand;
-
-  Function? onLeaf;
-
-  @override
-  void expand() {
-    if (onExpand != null) {
-      onExpand!(this);
-      controller!.selectedLeaf = null;
-    }
-    super.expand();
-  }
-
-  @override
-  void leaf() {
-    if (isObject) {
-      final objectReference = this as ObjectReference;
-      if (controller!.selectedAnalysisLeaf != null) {
-        controller!.selectedAnalysisLeaf = null;
-      }
-      controller!.selectedLeaf = objectReference.instance;
-    } else if (isAnalysis && this is AnalysisInstance) {
-      final AnalysisInstance analysisInstance = this as AnalysisInstance;
-      if (controller!.selectedLeaf != null) {
-        controller!.selectedLeaf = null;
-      }
-      controller!.selectedAnalysisLeaf = analysisInstance;
-    }
-
-    if (onLeaf != null) onLeaf!(this);
-
-    super.leaf();
-  }
-
-  @override
-  Reference shallowCopy() {
-    throw UnimplementedError(
-      'This method is not implemented. Implement if you '
-      'need to call `shallowCopy` on an instance of this class.',
-    );
-  }
-}
-
-/// Container of all snapshot analyses processed.
-class AnalysesReference extends Reference {
-  AnalysesReference()
-      : super.analysis(
-          null,
-          'Analysis',
-        );
-}
-
-/// Snapshot being analyzed.
-class AnalysisSnapshotReference extends Reference {
-  AnalysisSnapshotReference(
-    this.dateTime,
-  ) : super.analysis(
-          null,
-          'Analyzed ${MemoryController.formattedTimestamp(dateTime)}',
-        );
-
-  final DateTime dateTime;
-}
-
-/// Analysis data.
-class AnalysisReference extends Reference {
-  AnalysisReference(
-    String name, {
-    this.countNote,
-    this.sizeNote,
-  }) : super.analysis(
-          null,
-          name,
-        );
-
-  int? countNote;
-  int? sizeNote;
-}
-
-/// Analysis instance.
-class AnalysisInstance extends Reference {
-  AnalysisInstance(
-    MemoryController? controller,
-    String name,
-    this.fieldsRoot,
-  ) : super.analysis(
-          controller,
-          name,
-        );
-
-  /// quick view of fields analysis.
-  final AnalysisField fieldsRoot;
-}
-
-/// Analysis instance.
-class AnalysisField extends TreeNode<AnalysisField> {
-  AnalysisField(
-    this.name,
-    this.value,
-  );
-
-  AnalysisField._empty()
-      : name = null,
-        value = null;
-
-  static AnalysisField empty = AnalysisField._empty();
-
-  bool get isEmptyReference => this == empty;
-
-  final String? name;
-  final String? value;
-
-  @override
-  AnalysisField shallowCopy() {
-    throw UnimplementedError(
-      'This method is not implemented. Implement if you '
-      'need to call `shallowCopy` on an instance of this class.',
-    );
-  }
-}
-
-/// Snapshot being analyzed.
-class SnapshotReference extends Reference {
-  SnapshotReference(this.snapshot)
-      : super.snapshot(
-          null,
-          title(snapshot),
-        );
-
-  static String title(Snapshot snapshot) {
-    final timestamp = snapshot.collectedTimestamp;
-    final displayTimestamp = MemoryController.formattedTimestamp(timestamp);
-    return snapshot.autoSnapshot
-        ? 'Snapshot (${snapshot.snapshotGraph.name}) $displayTimestamp Auto'
-        : 'Snapshot (${snapshot.snapshotGraph.name}) $displayTimestamp';
-  }
-
-  final Snapshot snapshot;
-}
-
-class LibraryReference extends Reference {
-  LibraryReference(
-    MemoryController controller,
-    String libraryName,
-    this.actualClasses,
-  ) : super.library(
-          controller,
-          libraryName,
-          onExpand: (reference) {
-            assert(reference.isLibrary);
-
-            // Need to construct the children.
-            if (reference.children.isNotEmpty &&
-                reference.children.first.isEmptyReference) {
-              reference.children.clear();
-
-              final libraryReference = reference as LibraryReference;
-              for (final actualClass in libraryReference.actualClasses ?? {}) {
-                final classRef = ClassReference(controller, actualClass);
-                reference.addChild(classRef);
-
-                // Add place holders for the instances.
-                final instances =
-                    actualClass.getInstances(controller.heapGraph);
-                classRef.addAllChildren(
-                  List.filled(
-                    instances.length,
-                    Reference.empty,
-                  ),
-                );
-              }
-            }
-          },
-        );
-
-  Set<HeapGraphClassLive>? actualClasses;
-}
-
-class ExternalReferences extends Reference {
-  ExternalReferences(MemoryController controller, int externalsSize)
-      : super.externals(controller);
-}
-
-class ExternalReference extends Reference {
-  ExternalReference(MemoryController controller, String name, this.liveExternal)
-      : super.external(
-          controller,
-          name,
-          onExpand: (reference) {
-            assert(reference.isExternal);
-
-            // Need to construct the children.
-            if (reference.children.isNotEmpty &&
-                reference.children.first.isEmptyReference) {
-              reference.children.clear();
-
-              final externalReference = reference as ExternalReference;
-              final liveElement = externalReference.liveExternal.live;
-              externalReference.addChild(
-                ObjectReference(
-                  controller,
-                  0,
-                  liveElement,
-                ),
-              );
-            }
-          },
-        );
-
-  final HeapGraphExternalLive liveExternal;
-  int sumExternalSizes = 0;
-}
-
-class FilteredReference extends Reference {
-  FilteredReference(MemoryController controller) : super.filtered(controller);
-}
-
-void computeInstanceForClassReference(
-  MemoryController? controller,
-  Reference reference,
-) {
-  // Need to construct the children if the first child is Reference.empty.
-  if (reference.children.isNotEmpty &&
-      reference.children.first.isEmptyReference) {
-    reference.children.clear();
-
-    final classReference = reference as ClassReference;
-
-    final instances =
-        classReference.actualClass!.getInstances(controller!.heapGraph);
-
-    for (var index = 0; index < instances.length; index++) {
-      final instance = instances[index];
-      final objectRef = ObjectReference(controller, index, instance);
-      classReference.addChild(objectRef);
-    }
-  }
-}
-
-class ClassReference extends Reference {
-  ClassReference(
-    MemoryController controller,
-    HeapGraphClassLive actualClass,
-  ) : super.aClass(
-          controller,
-          actualClass,
-          onExpand: (reference) {
-            // Insure the children have been computed.
-            computeInstanceForClassReference(controller, reference);
-          },
-        );
-
-  List<HeapGraphElementLive> get instances =>
-      actualClass!.getInstances(controller!.heapGraph);
-}
-
-class ObjectReference extends Reference {
-  ObjectReference(
-    MemoryController? controller,
-    int index,
-    this.instance,
-  ) : super.object(controller, 'Instance $index');
-
-  final HeapGraphElementLive instance;
-}
-
-class ExternalObjectReference extends ObjectReference {
-  ExternalObjectReference(
-    MemoryController controller,
-    int index,
-    HeapGraphElementLive instance,
-    this.externalSize,
-  ) : super(
-          controller,
-          index,
-          instance,
-        );
-
-  final int externalSize;
-}
-
-class Snapshot {
-  Snapshot(
-    this.collectedTimestamp,
-    this.controller,
-    this.snapshotGraph,
-    this.libraryRoot,
-    this.autoSnapshot,
-  );
-
-  final bool autoSnapshot;
-  final MemoryController controller;
-  final DateTime collectedTimestamp;
-  final HeapSnapshotGraph snapshotGraph;
-  LibraryReference? libraryRoot;
-
-  final Map<String, LibraryReference> libraries = {};
-
-  List<LibraryReference> librariesToList() =>
-      libraries.entries.map((entry) => entry.value).toList();
-
-  List<HeapSnapshotClass> get classes => snapshotGraph.classes;
-}
-
-/// Base class for inspecting an instance field type, field name, and value.
-class FieldReference extends TreeNode<FieldReference> {
-  FieldReference._empty()
-      : controller = null,
-        instance = null,
-        name = emptyName,
-        isScaler = false,
-        isObject = false,
-        type = null,
-        value = null;
-
-  FieldReference._sentinel()
-      : controller = null,
-        instance = null,
-        name = sentinelName,
-        isScaler = false,
-        isObject = false,
-        type = null,
-        value = null;
-
-  FieldReference.createScaler(
-    this.controller,
-    this.instance,
-    this.type,
-    this.name,
-    this.value,
-  )   : isScaler = true,
-        isObject = false;
-
-  FieldReference.createObject(
-    this.controller,
-    this.instance,
-    this.type,
-    this.name, {
-    this.onExpand,
-  })  : value = null,
-        isScaler = false,
-        isObject = true;
-
-  static FieldReference empty = FieldReference._empty();
-
-  bool get isEmptyReference => this == empty;
-
-  static FieldReference sentinel = FieldReference._sentinel();
-
-  bool get isSentinelReference => this == sentinel;
-
-  final MemoryController? controller;
-
-  final HeapGraphElementLive? instance;
-
-  final String? type;
-
-  final String name;
-
-  final String? value;
-
-  final bool isScaler;
-
-  final bool isObject;
-
-  Function? onExpand;
-
-  @override
-  void expand() {
-    if (onExpand != null) {
-      onExpand!(this);
-    }
-    super.expand();
-  }
-
-  @override
-  FieldReference shallowCopy() {
-    throw UnimplementedError(
-      'This method is not implemented. Implement if you '
-      'need to call `shallowCopy` on an instance of this class.',
-    );
-  }
-}
-
-class ScalarFieldReference extends FieldReference {
-  ScalarFieldReference(
-    MemoryController controller,
-    HeapGraphElementLive instance,
-    String type,
-    String name,
-    String value,
-  ) : super.createScaler(
-          controller,
-          instance,
-          type,
-          name,
-          value,
-        );
-}
-
-class ObjectFieldReference extends FieldReference {
-  ObjectFieldReference(
-    MemoryController? controller,
-    HeapGraphElementLive? instance,
-    String type,
-    String name, {
-    this.isNull = false,
-  }) : super.createObject(
-          controller,
-          instance,
-          type,
-          name,
-        ) {
-    onExpand = _expandObjectFieldRef;
-  }
-
-  void _expandObjectFieldRef(reference) {
-    // Need to construct the children.
-    if (reference.children.isNotEmpty &&
-        reference.children.first.isEmptyReference) {
-      // Remove empty entries compute the real values.
-      reference.children.clear();
-
-      // Null value nothing to expand.
-      if (reference.isScaler) return;
-    }
-
-    assert(reference.isObject);
-    final ObjectFieldReference objectFieldReference = reference;
-
-    var objFields = instanceToFieldNodes(
-      controller,
-      objectFieldReference.instance!,
-    );
-
-    if (objFields.isNotEmpty) {
-      final computedFields = <FieldReference>[];
-      for (final ref in objFields) {
-        // Check if aready computed/expanded then skip.
-        final exist = objectFieldReference.children
-            .singleWhereOrNull((element) => element.name == ref.name);
-        if (exist == null) {
-          // Doesn't exist so add field.
-          if (ref is ObjectFieldReference && !ref.isNull) {
-            computedFields.add(ref);
-          } else {
-            final FieldReference fieldRef = ref;
-            final HeapGraphElementLive live = fieldRef.instance!;
-            final HeapGraphClassLive theClass =
-                live.theClass as HeapGraphClassLive;
-            final predefined = predefinedClasses[theClass.fullQualifiedName];
-            if (predefined != null && predefined.isScalar) {
-              final scalarValue = createScalar(
-                controller,
-                fieldRef.name,
-                fieldRef.instance!,
-              );
-              computedFields.add(scalarValue);
-            }
-          }
-        }
-      }
-      objFields = computedFields;
-    }
-
-    objectFieldReference.addAllChildren(objFields);
-  }
-
-  final bool isNull;
-}
-
-/// Return list of FieldReference nodes (TableTree use) from the fields of an instance.
-List<FieldReference> instanceToFieldNodes(
-  MemoryController? controller,
-  HeapGraphElementLive instance,
-) {
-  final List<FieldReference> root = [];
-  final List<MapEntry<String, HeapGraphElement>> fields = instance.getFields();
-
-  var sentinelCount = 0;
-  var fieldIndex = 0;
-  for (var fieldElement in fields) {
-    // Ignore internal patching fields.
-    if (fieldElement.key != '__parts') {
-      if (fieldElement.value is! HeapGraphElementSentinel &&
-          instance.references![fieldIndex] is! HeapGraphElementSentinel) {
-        root.add(
-          fieldToFieldReference(
-            controller,
-            instance,
-            fieldElement,
-          ),
-        );
-      } else {
-        sentinelCount++;
-        root.add(FieldReference.sentinel);
-      }
-    }
-    fieldIndex++;
-  }
-
-  if (root.isNotEmpty && sentinelCount > 0) {
-    root.removeWhere((e) => e.isSentinelReference);
-  }
-
-  return root;
-}
-
-/// Return a FieldReference node (TableTree use) from the field of an instance.
-FieldReference fieldToFieldReference(
-  MemoryController? controller,
-  HeapGraphElementLive instance,
-  MapEntry<String, HeapGraphElement?> fieldElement,
-) {
-  if (fieldElement.value is HeapGraphElementSentinel) {
-    // TODO(terry): Debug for now, eliminate, user's don't need to know.
-    return FieldReference.sentinel;
-  }
-
-  final theGraph = controller!.heapGraph;
-
-  final actual = fieldElement.value as HeapGraphElementLive;
-  final HeapGraphClassLive? theClass = actual.theClass as HeapGraphClassLive?;
-  // Debugging a particular field displayed use fieldElement.key (field name)
-  // to break.
-
-  // Handle change in VM service to use 0 based vs/ 1 based indexing. Need to
-  // support old semantics for old VMs.
-  final classId = actual.origin.classId;
-  final int indexIntoClass = classId;
-  if (!controller.heapGraph!.builtInClasses.containsValue(indexIntoClass)) {
-    return objectToFieldReference(
-      controller,
-      theGraph,
-      fieldElement,
-      actual,
-    );
-  } else {
-    final data = actual.origin.data;
-    if (data.runtimeType == HeapSnapshotObjectLengthData) {
-      final isAMap = isBuiltInMap(theClass!);
-      if (isAMap || isBuiltInList(theClass)) {
-        return listToFieldEntries(
-          controller,
-          actual,
-          fieldElement.key,
-          data.length,
-          isHashMap: isAMap,
-        );
-      }
-    } else if (isBuiltInHashMap(theClass!)) {
-      for (var ref in fieldElement.value!.references!) {
-        if (ref is! HeapGraphElementSentinel) {
-          final HeapGraphElementLive actual = ref as HeapGraphElementLive;
-          final HeapGraphClassLive theClass =
-              actual.theClass as HeapGraphClassLive;
-          if (isBuiltInList(theClass)) {
-            final hashMapData = actual.origin.data;
-            if (hashMapData.runtimeType == HeapSnapshotObjectLengthData) {
-              return listToFieldEntries(
-                controller,
-                ref,
-                fieldElement.key,
-                hashMapData.length ~/ 2,
-                isHashMap: true,
-              );
-            }
-          }
-        }
-      }
-    }
-    return createScalar(controller, fieldElement.key, actual);
-  }
-}
-
-/// Create a scalar field for display in the Table Tree.
-FieldReference createScalar(
-  MemoryController? controller,
-  String fieldName,
-  HeapGraphElementLive actual,
-) {
-  final data = actual.origin.data;
-
-  String dataValue;
-  String dataType = '';
-  switch (data.runtimeType) {
-    case HeapSnapshotObjectNoData:
-      dataValue = 'Object No Data';
-      break;
-    case HeapSnapshotObjectNullData:
-      dataValue = '';
-      dataType = 'Null';
-      break;
-    case HeapSnapshotObjectLengthData:
-      dataValue = data.length.toString();
-      break;
-    default:
-      final originClassId = actual.origin.classId;
-      final classId = originClassId;
-      dataValue = data.toString();
-      final dataTypeClass = controller!.heapGraph!.classes[classId];
-      if (dataTypeClass != null) {
-        final predefined = predefinedClasses[dataTypeClass.fullQualifiedName]!;
-        dataType = predefined.prettyName;
-      }
-  }
-
-  return FieldReference.createScaler(
-    controller,
-    actual,
-    dataType,
-    fieldName,
-    dataValue,
-  );
-}
-
-/// Display a List.
-FieldReference listToFieldEntries(
-  MemoryController? controller,
-  HeapGraphElement? reference,
-  String fieldName,
-  int? size, {
-  isHashMap = false,
-}) {
-  bool isAMap = false;
-  HeapGraphElementLive? actualListElement;
-  late ObjectFieldReference listObjectReference;
-  if (reference is HeapGraphElementLive) {
-    final actualListClass = reference.theClass as HeapGraphClassLive;
-    if (isBuiltInList(actualListClass)) {
-      // Add the list entry.
-      actualListElement = reference;
-      listObjectReference = ObjectFieldReference(
-        controller,
-        actualListElement,
-        isHashMap ? 'HashMap' : 'List',
-        isHashMap ? '$fieldName {$size}' : '[$size]',
-      );
-    } else if (isBuiltInMap(actualListClass)) {
-      // Add the Map field name and the key/value pairs.
-      actualListElement = reference;
-      listObjectReference = ObjectFieldReference(
-        controller,
-        actualListElement,
-        'Map',
-        '$fieldName { ${size! ~/ 2} }',
-      );
-
-      // Look for list of Map values.
-      for (final reference in actualListElement.references!) {
-        if (reference is HeapGraphElementLive) {
-          final HeapGraphClassLive theClass =
-              reference.theClass as HeapGraphClassLive;
-          final fullClassName = theClass.fullQualifiedName;
-          if (fullClassName == predefinedList) {
-            actualListElement = reference;
-            isAMap = true;
-            break;
-          }
-        }
-      }
-    }
-  }
-
-  var listIndex = 0;
-  final allEntryReferences = actualListElement!.references!;
-  final referencesLength = allEntryReferences.length;
-
-  // Find all the Map key/value pairs (for integer keys the key maybe missing).
-  // TODO(terry): Need to verify.
-  final List<HeapGraphElement> realEntries = [];
-  for (var entryElementIndex = 0;
-      entryElementIndex < referencesLength;
-      entryElementIndex++) {
-    final entry = allEntryReferences[entryElementIndex];
-    if (entry is HeapGraphElementLive) {
-      final HeapGraphElementLive entryElement = entry;
-      final HeapGraphClassLive actualClass =
-          entryElement.theClass as HeapGraphClassLive;
-      if (actualClass.fullQualifiedName != predefinedNull) {
-        realEntries.add(entryElement);
-      }
-    }
-  }
-
-  // TODO(terry): Need to verify.
-  // Only value key if size != to number of real entries.
-  final hasKeyValues = isAMap && realEntries.length == size;
-
-  for (var realEntryIndex = 0;
-      realEntryIndex < realEntries.length;
-      realEntryIndex++) {
-    final entryElement = realEntries[realEntryIndex];
-    if (entryElement is HeapGraphElementLive) {
-      final entryClass = entryElement.theClass;
-      if (entryClass is HeapGraphClassLive &&
-          entryClass.fullQualifiedName != predefinedNull) {
-        final predefined0 = predefinedClasses[entryClass.fullQualifiedName];
-        FieldReference? listEntry;
-        if (predefined0 != null && predefined0.isScalar) {
-          if (isAMap) {
-            if (hasKeyValues) {
-              final HeapGraphElementLive valueElement =
-                  realEntries[realEntryIndex + 1] as HeapGraphElementLive;
-              realEntryIndex++;
-              // The value entry is computed on key expansion.
-              final predefined1 =
-                  predefinedClasses[entryClass.fullQualifiedName]!;
-              listEntry = ObjectFieldReference(
-                controller,
-                entryElement,
-                '${predefined1.prettyName}',
-                'key \'${entryElement.origin.data}\'',
-              );
-
-              FieldReference valueEntry;
-              final HeapGraphClassLive valueClass =
-                  valueElement.theClass as HeapGraphClassLive;
-              final predefined2 =
-                  predefinedClasses[valueClass.fullQualifiedName];
-              if (predefined2 != null && predefined2.isScalar) {
-                valueEntry = createScalar(controller, 'value', valueElement);
-              } else {
-                valueEntry = ObjectFieldReference(
-                  controller,
-                  valueElement,
-                  valueClass.name,
-                  'value',
-                );
-                // Compute the object's fields when onExpand hit.
-                valueEntry.addChild(FieldReference.empty);
-              }
-              listEntry.addChild(valueEntry);
-            } else {
-              // This is the value w/o any idea the index is the key.
-              listEntry =
-                  createScalar(controller, 'value $listIndex', entryElement);
-            }
-          } else {
-            // Display the scalar entry.
-            listEntry = createScalar(controller, fieldName, entryElement);
-          }
-        } else {
-          listEntry ??= ObjectFieldReference(
-            controller,
-            entryElement,
-            entryClass.name,
-            '[$listIndex]',
-          );
-          if (entryElement.references!.isNotEmpty) {
-            // Key of the Map is an object.
-            if (isAMap) {
-              final keyFields = entryElement.getFields();
-              for (final keyField in keyFields) {
-                final key = keyField.key;
-                final value = keyField.value;
-
-                // Skip sentinels and null values.
-                if (!value.isSentinel &&
-                    !dataIsNull(value as HeapGraphElementLive)) {
-                  final HeapGraphElementLive live = value;
-                  final HeapGraphClassLive theClass =
-                      live.theClass as HeapGraphClassLive;
-                  final className = theClass.fullQualifiedName;
-                  final predefined = predefinedClasses[className];
-                  if (predefined != null && predefined.isScalar) {
-                    final scalarEntry = createScalar(controller, key, live);
-                    listEntry.addChild(scalarEntry);
-                  } else {
-                    final keyObjectRef = ObjectFieldReference(
-                      controller,
-                      live,
-                      theClass.name,
-                      '$key',
-                    );
-
-                    keyObjectRef.addChild(FieldReference.empty);
-                    listEntry.addChild(keyObjectRef);
-                  }
-                }
-              }
-            }
-          }
-        }
-
-        // Entry type to expand later.
-        if (!isAMap) {
-          listEntry.addChild(FieldReference.empty);
-        }
-
-        // Add our [n] entry.
-        listObjectReference.addChild(listEntry);
-
-        // TODO(terry): Consider showing all entries - is it useful?
-        // Add each entry to the list, up to 100.
-        if (listIndex++ > 100) break;
-      }
-    }
-  }
-
-  return listObjectReference;
-}
-
-bool dataIsNull(HeapGraphElementLive live) =>
-    live.origin.data.runtimeType == HeapSnapshotObjectNullData;
-
-/// Return a ObjectFieldReference node (TableTree use) from the field that
-/// is an object (instance).  This object's field will be computed when
-/// the ObjectFieldReference node is expanded.
-FieldReference objectToFieldReference(
-  MemoryController? controller,
-  HeapGraph? theGraph,
-  MapEntry<String, HeapGraphElement?> objectEntry,
-  HeapGraphElementLive actual,
-) {
-  final elementActual = objectEntry.value as HeapGraphElementLive;
-  final classActual = elementActual.theClass as HeapGraphClassLive;
-
-  final isNullValue = actual.origin.data == null;
-  final reference = ObjectFieldReference(
-    controller,
-    actual,
-    classActual.name,
-    objectEntry.key,
-    isNull: isNullValue,
-  );
-
-  // If the object isn't null then add one fake empty child, so node
-  // can be expanded.  The object's fields are computed on expansion.
-  reference.addAllChildren(
-    List.filled(
-      isNullValue ? 0 : 1,
-      FieldReference.empty,
-    ),
-  );
-
-  return reference;
-}
diff --git a/packages/devtools_app/lib/src/screens/memory/memory_tabs.dart b/packages/devtools_app/lib/src/screens/memory/memory_tabs.dart
new file mode 100644
index 0000000..4dd9d53
--- /dev/null
+++ b/packages/devtools_app/lib/src/screens/memory/memory_tabs.dart
@@ -0,0 +1,171 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+
+import '../../primitives/auto_dispose_mixin.dart';
+import '../../shared/common_widgets.dart';
+import '../../shared/theme.dart';
+import '../../shared/utils.dart';
+import '../../ui/search.dart';
+import '../../ui/tab.dart';
+import 'memory_controller.dart';
+import 'panes/allocation_profile/allocation_profile_table_view.dart';
+import 'panes/allocation_tracing/allocation_profile_tracing_view.dart';
+import 'panes/diff/diff_pane.dart';
+import 'panes/leaks/leaks_pane.dart';
+
+@visibleForTesting
+class MemoryScreenKeys {
+  static const leaksTab = Key('Leaks Tab');
+  static const dartHeapTableProfileTab = Key('Dart Heap Profile Tab');
+  static const dartHeapAllocationTracingTab =
+      Key('Dart Heap Allocation Tracing Tab');
+  static const diffTab = Key('Diff Tab');
+}
+
+class MemoryTabs extends StatefulWidget {
+  const MemoryTabs(
+    this.controller,
+  );
+
+  final MemoryController controller;
+
+  @override
+  _MemoryTabsState createState() => _MemoryTabsState();
+}
+
+class _MemoryTabsState extends State<MemoryTabs>
+    with
+        AutoDisposeMixin,
+        ProvidedControllerMixin<MemoryController, MemoryTabs>,
+        SearchFieldMixin<MemoryTabs>,
+        TickerProviderStateMixin {
+  static const _gaPrefix = 'memoryTab';
+
+  late List<Tab> _tabs;
+  late TabController _tabController;
+  final ValueNotifier<int> _currentTab = ValueNotifier(0);
+
+  void _initTabs() {
+    _tabs = [
+      DevToolsTab.create(
+        key: MemoryScreenKeys.dartHeapTableProfileTab,
+        tabName: 'Profile',
+        gaPrefix: _gaPrefix,
+      ),
+      DevToolsTab.create(
+        key: MemoryScreenKeys.dartHeapAllocationTracingTab,
+        tabName: 'Allocation Tracing',
+        gaPrefix: _gaPrefix,
+      ),
+      DevToolsTab.create(
+        key: MemoryScreenKeys.diffTab,
+        gaPrefix: _gaPrefix,
+        tabName: 'Diff',
+      ),
+      if (widget.controller.shouldShowLeaksTab.value)
+        DevToolsTab.create(
+          key: MemoryScreenKeys.leaksTab,
+          gaPrefix: _gaPrefix,
+          tabName: 'Leaks',
+        ),
+    ];
+
+    _tabController = TabController(length: _tabs.length, vsync: this);
+    _tabController.addListener(_onTabChanged);
+  }
+
+  void _onTabChanged() => _currentTab.value = _tabController.index;
+
+  @override
+  void didChangeDependencies() {
+    super.didChangeDependencies();
+    if (!initController()) return;
+
+    cancelListeners();
+
+    _initTabs();
+
+    addAutoDisposeListener(controller.shouldShowLeaksTab, () {
+      setState(() {
+        _initTabs();
+      });
+    });
+  }
+
+  @override
+  void dispose() {
+    _tabController
+      ..removeListener(_onTabChanged)
+      ..dispose();
+
+    super.dispose();
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    final themeData = Theme.of(context);
+
+    return Column(
+      children: [
+        const SizedBox(height: defaultSpacing),
+        ValueListenableBuilder<int>(
+          valueListenable: _currentTab,
+          builder: (context, index, _) => Row(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            children: [
+              TabBar(
+                labelColor: themeData.textTheme.bodyLarge!.color,
+                isScrollable: true,
+                controller: _tabController,
+                tabs: _tabs,
+              ),
+            ],
+          ),
+        ),
+        const Divider(),
+        Expanded(
+          child: TabBarView(
+            physics: defaultTabBarViewPhysics,
+            controller: _tabController,
+            children: [
+              // Profile Tab
+              KeepAliveWrapper(
+                child: AllocationProfileTableView(
+                  controller: controller.allocationProfileController,
+                ),
+              ),
+              const KeepAliveWrapper(
+                child: AllocationProfileTracingView(),
+              ),
+              // Diff tab.
+              KeepAliveWrapper(
+                child: DiffPane(
+                  diffController: controller.diffPaneController,
+                ),
+              ),
+              // Leaks tab.
+              if (controller.shouldShowLeaksTab.value)
+                const KeepAliveWrapper(child: LeaksPane()),
+            ],
+          ),
+        ),
+      ],
+    );
+  }
+
+  Widget tableExample(IconData? iconData, String entry) {
+    final themeData = Theme.of(context);
+    return Row(
+      mainAxisAlignment: MainAxisAlignment.center,
+      children: [
+        iconData == null
+            ? Text(' ', style: themeData.fixedFontStyle)
+            : Icon(iconData),
+        Text(entry, style: themeData.fixedFontStyle),
+      ],
+    );
+  }
+}
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/chart/chart_control_pane.dart b/packages/devtools_app/lib/src/screens/memory/panes/chart/chart_control_pane.dart
index 16b71f7..a7c322a 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/chart/chart_control_pane.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/chart/chart_control_pane.dart
@@ -87,13 +87,6 @@
 
     controller.memoryTimeline.reset();
 
-    // Clear all analysis and snapshots collected too.
-    controller.clearAllSnapshots();
-    controller.classRoot = null;
-    controller.topNode = null;
-    controller.selectedSnapshotTimestamp = null;
-    controller.selectedLeaf = null;
-
     // Remove history of all plotted data in all charts.
     widget.chartController.resetAll();
   }
diff --git a/packages/devtools_app/lib/src/screens/memory/panes/control/settings_dialog.dart b/packages/devtools_app/lib/src/screens/memory/panes/control/settings_dialog.dart
index 3d11fa3..39537e5 100644
--- a/packages/devtools_app/lib/src/screens/memory/panes/control/settings_dialog.dart
+++ b/packages/devtools_app/lib/src/screens/memory/panes/control/settings_dialog.dart
@@ -14,7 +14,6 @@
 @visibleForTesting
 class MemorySettingDialogKeys {
   static const Key showAndroidChartCheckBox = ValueKey('showAndroidChart');
-  static const Key autoSnapshotCheckbox = ValueKey('autoSnapshotCheckbox');
 }
 
 class MemorySettingsDialog extends StatelessWidget {
@@ -39,14 +38,6 @@
                   'Show Android memory chart in addition to Dart memory chart',
               checkboxKey: MemorySettingDialogKeys.showAndroidChartCheckBox,
             ),
-            const SizedBox(
-              height: defaultSpacing,
-            ),
-            CheckboxSetting(
-              notifier: preferences.memory.autoSnapshotEnabled,
-              title: 'Automatically take snapshot when memory usage spikes',
-              checkboxKey: MemorySettingDialogKeys.autoSnapshotCheckbox,
-            ),
           ],
         ),
       ),
diff --git a/packages/devtools_app/lib/src/shared/preferences.dart b/packages/devtools_app/lib/src/shared/preferences.dart
index 8469f11..cb2acfa 100644
--- a/packages/devtools_app/lib/src/shared/preferences.dart
+++ b/packages/devtools_app/lib/src/shared/preferences.dart
@@ -310,9 +310,6 @@
   static const _androidCollectionEnabledStorageId =
       'memory.androidCollectionEnabled';
 
-  final autoSnapshotEnabled = ValueNotifier<bool>(false);
-  static const _autoSnapshotEnabledStorageId = 'memory.autoSnapshotEnabled';
-
   final showChart = ValueNotifier<bool>(true);
   static const _showChartStorageId = 'memory.showChart';
 
@@ -336,24 +333,6 @@
         await storage.getValue(_androidCollectionEnabledStorageId) == 'true';
 
     addAutoDisposeListener(
-      autoSnapshotEnabled,
-      () {
-        storage.setValue(
-          _autoSnapshotEnabledStorageId,
-          autoSnapshotEnabled.value.toString(),
-        );
-        if (autoSnapshotEnabled.value) {
-          ga.select(
-            analytics_constants.memory,
-            analytics_constants.MemoryEvent.autoSnapshot,
-          );
-        }
-      },
-    );
-    autoSnapshotEnabled.value =
-        await storage.getValue(_autoSnapshotEnabledStorageId) == 'true';
-
-    addAutoDisposeListener(
       showChart,
       () {
         storage.setValue(
diff --git a/packages/devtools_app/test/goldens/settings_dialog_default.png b/packages/devtools_app/test/goldens/settings_dialog_default.png
index 609bf6c..5334987 100644
--- a/packages/devtools_app/test/goldens/settings_dialog_default.png
+++ b/packages/devtools_app/test/goldens/settings_dialog_default.png
Binary files differ
diff --git a/packages/devtools_app/test/goldens/settings_dialog_modified.png b/packages/devtools_app/test/goldens/settings_dialog_modified.png
index fc69ac1..174a86a 100644
--- a/packages/devtools_app/test/goldens/settings_dialog_modified.png
+++ b/packages/devtools_app/test/goldens/settings_dialog_modified.png
Binary files differ
diff --git a/packages/devtools_app/test/memory/allocation_profile/allocation_profile_table_view_test.dart b/packages/devtools_app/test/memory/allocation_profile/allocation_profile_table_view_test.dart
index d200592..d3385e4 100644
--- a/packages/devtools_app/test/memory/allocation_profile/allocation_profile_table_view_test.dart
+++ b/packages/devtools_app/test/memory/allocation_profile/allocation_profile_table_view_test.dart
@@ -3,8 +3,8 @@
 // found in the LICENSE file.
 
 import 'package:devtools_app/src/screens/memory/memory_controller.dart';
-import 'package:devtools_app/src/screens/memory/memory_heap_tree_view.dart';
 import 'package:devtools_app/src/screens/memory/memory_screen.dart';
+import 'package:devtools_app/src/screens/memory/memory_tabs.dart';
 import 'package:devtools_app/src/screens/memory/panes/allocation_profile/allocation_profile_table_view_controller.dart';
 import 'package:devtools_app/src/screens/memory/panes/allocation_profile/model.dart';
 import 'package:devtools_app/src/shared/globals.dart';
diff --git a/packages/devtools_app/test/memory/allocation_tracing/allocation_profile_tracing_view_test.dart b/packages/devtools_app/test/memory/allocation_tracing/allocation_profile_tracing_view_test.dart
index eb66897..e632f5b 100644
--- a/packages/devtools_app/test/memory/allocation_tracing/allocation_profile_tracing_view_test.dart
+++ b/packages/devtools_app/test/memory/allocation_tracing/allocation_profile_tracing_view_test.dart
@@ -9,8 +9,8 @@
 import 'package:devtools_app/src/config_specific/import_export/import_export.dart';
 import 'package:devtools_app/src/primitives/trees.dart';
 import 'package:devtools_app/src/screens/memory/memory_controller.dart';
-import 'package:devtools_app/src/screens/memory/memory_heap_tree_view.dart';
 import 'package:devtools_app/src/screens/memory/memory_screen.dart';
+import 'package:devtools_app/src/screens/memory/memory_tabs.dart';
 import 'package:devtools_app/src/screens/memory/panes/allocation_tracing/allocation_profile_tracing_tree.dart';
 import 'package:devtools_app/src/screens/memory/panes/allocation_tracing/allocation_profile_tracing_view.dart';
 import 'package:devtools_app/src/screens/memory/panes/allocation_tracing/allocation_profile_tracing_view_controller.dart';
diff --git a/packages/devtools_app/test/memory/control/settings_dialog_test.dart b/packages/devtools_app/test/memory/control/settings_dialog_test.dart
index 3bc8908..3f7ba8e 100644
--- a/packages/devtools_app/test/memory/control/settings_dialog_test.dart
+++ b/packages/devtools_app/test/memory/control/settings_dialog_test.dart
@@ -53,13 +53,8 @@
       preferences.memory.androidCollectionEnabled.value,
       isFalse,
     );
-    expect(
-      preferences.memory.autoSnapshotEnabled.value,
-      isFalse,
-    );
     await tester
         .tap(find.byKey(MemorySettingDialogKeys.showAndroidChartCheckBox));
-    await tester.tap(find.byKey(MemorySettingDialogKeys.autoSnapshotCheckbox));
     await tester.pumpAndSettle();
     await expectLater(
       find.byType(MemorySettingsDialog),
@@ -69,10 +64,6 @@
       preferences.memory.androidCollectionEnabled.value,
       isTrue,
     );
-    expect(
-      preferences.memory.autoSnapshotEnabled.value,
-      isTrue,
-    );
 
     // Reopen the dialog and check the settings are not changed.
     await tester.tap(find.byType(DialogCloseButton));
diff --git a/packages/devtools_app/test/memory/diff/widgets/diff_pane_test.dart b/packages/devtools_app/test/memory/diff/widgets/diff_pane_test.dart
index e886004..707b6c3 100644
--- a/packages/devtools_app/test/memory/diff/widgets/diff_pane_test.dart
+++ b/packages/devtools_app/test/memory/diff/widgets/diff_pane_test.dart
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'package:devtools_app/src/screens/memory/memory_heap_tree_view.dart';
 import 'package:devtools_app/src/screens/memory/memory_screen.dart';
+import 'package:devtools_app/src/screens/memory/memory_tabs.dart';
 import 'package:devtools_app/src/screens/memory/panes/diff/diff_pane.dart';
 import 'package:devtools_test/devtools_test.dart';
 import 'package:flutter/material.dart';
diff --git a/packages/devtools_app/test/shared/preferences_controller_test.dart b/packages/devtools_app/test/shared/preferences_controller_test.dart
index f42dd08..bde9dbd 100644
--- a/packages/devtools_app/test/shared/preferences_controller_test.dart
+++ b/packages/devtools_app/test/shared/preferences_controller_test.dart
@@ -136,25 +136,18 @@
       await controller.init();
     });
 
-    test('has expected default values', () async {
-      expect(controller.androidCollectionEnabled.value, isFalse);
-      expect(controller.autoSnapshotEnabled.value, isFalse);
-    });
-
     test('stores values and reads them on init', () async {
       storage.values.clear();
 
       // Remember original values.
       final originalAndroidCollection =
           controller.androidCollectionEnabled.value;
-      final originalAutoSnapshot = controller.androidCollectionEnabled.value;
 
       // Flip the values in controller.
       controller.androidCollectionEnabled.value = !originalAndroidCollection;
-      controller.autoSnapshotEnabled.value = !originalAutoSnapshot;
 
       // Check the values are stored.
-      expect(storage.values, hasLength(2));
+      expect(storage.values, hasLength(1));
 
       // Reload the values from storage.
       await controller.init();
@@ -164,10 +157,6 @@
         controller.androidCollectionEnabled.value,
         !originalAndroidCollection,
       );
-      expect(
-        controller.autoSnapshotEnabled.value,
-        !originalAutoSnapshot,
-      );
 
       // Flip the values in storage.
       for (var key in storage.values.keys) {
@@ -182,10 +171,6 @@
         controller.androidCollectionEnabled.value,
         originalAndroidCollection,
       );
-      expect(
-        controller.autoSnapshotEnabled.value,
-        originalAutoSnapshot,
-      );
     });
   });