all_elements.dart 3.08 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'package:flutter/foundation.dart';
6 7 8 9 10 11 12 13 14 15 16 17 18
import 'package:flutter/widgets.dart';

/// Provides an iterable that efficiently returns all the elements
/// rooted at the given element. See [CachingIterable] for details.
///
/// This method must be called again if the tree changes. You cannot
/// call this function once, then reuse the iterable after having
/// changed the state of the tree, because the iterable returned by
/// this function caches the results and only walks the tree once.
///
/// The same applies to any iterable obtained indirectly through this
/// one, for example the results of calling `where` on this iterable
/// are also cached.
19 20
Iterable<Element> collectAllElementsFrom(
  Element rootElement, {
21
  required bool skipOffstage,
22
}) {
23
  return CachingIterable<Element>(_DepthFirstChildIterator(rootElement, skipOffstage));
24 25
}

26 27
/// Provides a recursive, efficient, depth first search of an element tree.
///
28
/// [Element.visitChildren] does not guarantee order, but does guarantee stable
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
/// order. This iterator also guarantees stable order, and iterates in a left
/// to right order:
///
///       1
///     /   \
///    2     3
///   / \   / \
///  4   5 6   7
///
/// Will iterate in order 2, 4, 5, 3, 6, 7.
///
/// Performance is important here because this method is on the critical path
/// for flutter_driver and package:integration_test performance tests.
/// Performance is measured in the all_elements_bench microbenchmark.
/// Any changes to this implementation should check the before and after numbers
/// on that benchmark to avoid regressions in general performance test overhead.
///
/// If we could use RTL order, we could save on performance, but numerous tests
/// have been written (and developers clearly expect) that LTR order will be
/// respected.
49
class _DepthFirstChildIterator implements Iterator<Element> {
50 51 52
  _DepthFirstChildIterator(Element rootElement, this.skipOffstage) {
    _fillChildren(rootElement);
  }
53 54

  final bool skipOffstage;
55

56
  late Element _current;
57

58
  final List<Element> _stack = <Element>[];
59 60 61 62 63 64

  @override
  Element get current => _current;

  @override
  bool moveNext() {
65
    if (_stack.isEmpty) {
66
      return false;
67
    }
68 69

    _current = _stack.removeLast();
70
    _fillChildren(_current);
71 72 73 74

    return true;
  }

75
  void _fillChildren(Element element) {
76
    assert(element != null);
77 78 79 80
    // If we did not have to follow LTR order and could instead use RTL,
    // we could avoid reversing this and the operation would be measurably
    // faster. Unfortunately, a lot of tests depend on LTR order.
    final List<Element> reversed = <Element>[];
81
    if (skipOffstage) {
82
      element.debugVisitOnstageChildren(reversed.add);
83
    } else {
84 85 86 87 88 89
      element.visitChildren(reversed.add);
    }
    // This is faster than _stack.addAll(reversed.reversed), presumably since
    // we don't actually care about maintaining an iteration pointer.
    while (reversed.isNotEmpty) {
      _stack.add(reversed.removeLast());
90
    }
91 92
  }
}