Skip to content

Commit c1a6c55

Browse files
Improve UI-thread animation performance (#159288)
The following PR (flutter/flutter#138481) got split in 2, this is part 2. We now have the microbenchmarks to compare this change against (and hopefully see improvements). Close: flutter/flutter#146211 Part 1: flutter/flutter#153368 --------- Co-authored-by: Nate Wilson <nathan.wilson1232@gmail.com>
1 parent 61d7d0e commit c1a6c55

File tree

3 files changed

+30
-6
lines changed

3 files changed

+30
-6
lines changed

packages/flutter/lib/src/animation/listener_helpers.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ mixin AnimationEagerListenerMixin {
9191
/// and [didUnregisterListener]. Implementations of these methods can be obtained
9292
/// by mixing in another mixin from this library, such as [AnimationLazyListenerMixin].
9393
mixin AnimationLocalListenersMixin {
94-
final ObserverList<VoidCallback> _listeners = ObserverList<VoidCallback>();
94+
final HashedObserverList<VoidCallback> _listeners = HashedObserverList<VoidCallback>();
9595

9696
/// Called immediately before a listener is added via [addListener].
9797
///

packages/flutter/lib/src/foundation/observer_list.dart

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,15 @@ class ObserverList<T> extends Iterable<T> {
4343
///
4444
/// Returns whether the item was present in the list.
4545
bool remove(T item) {
46-
_isDirty = true;
47-
_set.clear(); // Clear the set so that we don't leak items.
48-
return _list.remove(item);
46+
final bool removed = _list.remove(item);
47+
if (removed) {
48+
_isDirty = true;
49+
_set.clear(); // Clear the set so that we don't leak items.
50+
}
51+
return removed;
4952
}
5053

51-
/// Removes all items from the list.
54+
/// Removes all items from the [ObserverList].
5255
void clear() {
5356
_isDirty = false;
5457
_list.clear();
@@ -78,6 +81,10 @@ class ObserverList<T> extends Iterable<T> {
7881
@override
7982
bool get isNotEmpty => _list.isNotEmpty;
8083

84+
/// Creates a List containing the elements of the [ObserverList].
85+
///
86+
/// Overrides the default implementation of the [Iterable] to reduce number
87+
/// of allocations.
8188
@override
8289
List<T> toList({bool growable = true}) {
8390
return _list.toList(growable: growable);
@@ -126,6 +133,9 @@ class HashedObserverList<T> extends Iterable<T> {
126133
return true;
127134
}
128135

136+
/// Removes all items from the [HashedObserverList].
137+
void clear() => _map.clear();
138+
129139
@override
130140
bool contains(Object? element) => _map.containsKey(element);
131141

@@ -137,4 +147,18 @@ class HashedObserverList<T> extends Iterable<T> {
137147

138148
@override
139149
bool get isNotEmpty => _map.isNotEmpty;
150+
151+
/// Creates a List containing the elements of the [HashedObserverList].
152+
///
153+
/// Overrides the default implementation of [Iterable] to reduce number of
154+
/// allocations.
155+
@override
156+
List<T> toList({bool growable = true}) {
157+
final Iterator<T> iterator = _map.keys.iterator;
158+
return List<T>.generate(
159+
_map.length,
160+
(_) => (iterator..moveNext()).current,
161+
growable: growable,
162+
);
163+
}
140164
}

packages/flutter/test/animation/iteration_patterns_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ void main() {
4040
log.clear();
4141

4242
controller.value = 0.4;
43-
expect(log, <String>['listener2', 'listener4', 'listener4']);
43+
expect(log, <String>['listener2', 'listener4']);
4444
log.clear();
4545
});
4646

0 commit comments

Comments
 (0)