Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
10d5ec87
Unverified
Commit
10d5ec87
authored
Apr 14, 2021
by
Romain Rastel
Committed by
GitHub
Apr 14, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve the performances of ChangeNotifier (#71947)
parent
a219b86d
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
307 additions
and
55 deletions
+307
-55
change_notifier.dart
packages/flutter/lib/src/foundation/change_notifier.dart
+123
-21
change_notifier_test.dart
packages/flutter/test/foundation/change_notifier_test.dart
+184
-34
No files found.
packages/flutter/lib/src/foundation/change_notifier.dart
View file @
10d5ec87
...
@@ -2,8 +2,6 @@
...
@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'dart:collection'
;
import
'package:meta/meta.dart'
;
import
'package:meta/meta.dart'
;
import
'assertions.dart'
;
import
'assertions.dart'
;
...
@@ -94,11 +92,6 @@ abstract class ValueListenable<T> extends Listenable {
...
@@ -94,11 +92,6 @@ abstract class ValueListenable<T> extends Listenable {
T
get
value
;
T
get
value
;
}
}
class
_ListenerEntry
extends
LinkedListEntry
<
_ListenerEntry
>
{
_ListenerEntry
(
this
.
listener
);
final
VoidCallback
listener
;
}
/// A class that can be extended or mixed in that provides a change notification
/// A class that can be extended or mixed in that provides a change notification
/// API using [VoidCallback] for notifications.
/// API using [VoidCallback] for notifications.
///
///
...
@@ -109,11 +102,15 @@ class _ListenerEntry extends LinkedListEntry<_ListenerEntry> {
...
@@ -109,11 +102,15 @@ class _ListenerEntry extends LinkedListEntry<_ListenerEntry> {
///
///
/// * [ValueNotifier], which is a [ChangeNotifier] that wraps a single value.
/// * [ValueNotifier], which is a [ChangeNotifier] that wraps a single value.
class
ChangeNotifier
implements
Listenable
{
class
ChangeNotifier
implements
Listenable
{
LinkedList
<
_ListenerEntry
>?
_listeners
=
LinkedList
<
_ListenerEntry
>();
int
_count
=
0
;
List
<
VoidCallback
?>
_listeners
=
List
<
VoidCallback
?>.
filled
(
0
,
null
);
int
_notificationCallStackDepth
=
0
;
int
_reentrantlyRemovedListeners
=
0
;
bool
_debugDisposed
=
false
;
bool
_debugAssertNotDisposed
()
{
bool
_debugAssertNotDisposed
()
{
assert
(()
{
assert
(()
{
if
(
_
listeners
==
null
)
{
if
(
_
debugDisposed
)
{
throw
FlutterError
(
throw
FlutterError
(
'A
$runtimeType
was used after being disposed.
\n
'
'A
$runtimeType
was used after being disposed.
\n
'
'Once you have called dispose() on a
$runtimeType
, it can no longer be used.'
,
'Once you have called dispose() on a
$runtimeType
, it can no longer be used.'
,
...
@@ -142,7 +139,7 @@ class ChangeNotifier implements Listenable {
...
@@ -142,7 +139,7 @@ class ChangeNotifier implements Listenable {
@protected
@protected
bool
get
hasListeners
{
bool
get
hasListeners
{
assert
(
_debugAssertNotDisposed
());
assert
(
_debugAssertNotDisposed
());
return
_
listeners
!.
isNotEmpty
;
return
_
count
>
0
;
}
}
/// Register a closure to be called when the object changes.
/// Register a closure to be called when the object changes.
...
@@ -174,7 +171,48 @@ class ChangeNotifier implements Listenable {
...
@@ -174,7 +171,48 @@ class ChangeNotifier implements Listenable {
@override
@override
void
addListener
(
VoidCallback
listener
)
{
void
addListener
(
VoidCallback
listener
)
{
assert
(
_debugAssertNotDisposed
());
assert
(
_debugAssertNotDisposed
());
_listeners
!.
add
(
_ListenerEntry
(
listener
));
if
(
_count
==
_listeners
.
length
)
{
if
(
_count
==
0
)
{
_listeners
=
List
<
VoidCallback
?>.
filled
(
1
,
null
);
}
else
{
final
List
<
VoidCallback
?>
newListeners
=
List
<
VoidCallback
?>.
filled
(
_listeners
.
length
*
2
,
null
);
for
(
int
i
=
0
;
i
<
_count
;
i
++)
{
newListeners
[
i
]
=
_listeners
[
i
];
}
_listeners
=
newListeners
;
}
}
_listeners
[
_count
++]
=
listener
;
}
void
_removeAt
(
int
index
)
{
// The list holding the listeners is not growable for performances reasons.
// We still want to shrink this list if a lot of listeners have been added
// and then removed outside a notifyListeners iteration.
// We do this only when the real number of listeners is half the length
// of our list.
_count
-=
1
;
if
(
_count
*
2
<=
_listeners
.
length
)
{
final
List
<
VoidCallback
?>
newListeners
=
List
<
VoidCallback
?>.
filled
(
_count
,
null
);
// Listeners before the index are at the same place.
for
(
int
i
=
0
;
i
<
index
;
i
++)
newListeners
[
i
]
=
_listeners
[
i
];
// Listeners after the index move towards the start of the list.
for
(
int
i
=
index
;
i
<
_count
;
i
++)
newListeners
[
i
]
=
_listeners
[
i
+
1
];
_listeners
=
newListeners
;
}
else
{
// When there are more listeners than half the length of the list, we only
// shift our listeners, so that we avoid to reallocate memory for the
// whole list.
for
(
int
i
=
index
;
i
<
_count
;
i
++)
_listeners
[
i
]
=
_listeners
[
i
+
1
];
_listeners
[
_count
]
=
null
;
}
}
}
/// Remove a previously registered closure from the list of closures that are
/// Remove a previously registered closure from the list of closures that are
...
@@ -193,10 +231,22 @@ class ChangeNotifier implements Listenable {
...
@@ -193,10 +231,22 @@ class ChangeNotifier implements Listenable {
@override
@override
void
removeListener
(
VoidCallback
listener
)
{
void
removeListener
(
VoidCallback
listener
)
{
assert
(
_debugAssertNotDisposed
());
assert
(
_debugAssertNotDisposed
());
for
(
final
_ListenerEntry
entry
in
_listeners
!)
{
for
(
int
i
=
0
;
i
<
_count
;
i
++)
{
if
(
entry
.
listener
==
listener
)
{
final
VoidCallback
?
_listener
=
_listeners
[
i
];
entry
.
unlink
();
if
(
_listener
==
listener
)
{
return
;
if
(
_notificationCallStackDepth
>
0
)
{
// We don't resize the list during notifyListeners iterations
// but we set to null, the listeners we want to remove. We will
// effectively resize the list at the end of all notifyListeners
// iterations.
_listeners
[
i
]
=
null
;
_reentrantlyRemovedListeners
++;
}
else
{
// When we are outside the notifyListeners iterations we can
// effectively shrink the list.
_removeAt
(
i
);
}
break
;
}
}
}
}
}
}
...
@@ -210,7 +260,10 @@ class ChangeNotifier implements Listenable {
...
@@ -210,7 +260,10 @@ class ChangeNotifier implements Listenable {
@mustCallSuper
@mustCallSuper
void
dispose
()
{
void
dispose
()
{
assert
(
_debugAssertNotDisposed
());
assert
(
_debugAssertNotDisposed
());
_listeners
=
null
;
assert
(()
{
_debugDisposed
=
true
;
return
true
;
}());
}
}
/// Call all the registered listeners.
/// Call all the registered listeners.
...
@@ -232,15 +285,26 @@ class ChangeNotifier implements Listenable {
...
@@ -232,15 +285,26 @@ class ChangeNotifier implements Listenable {
@visibleForTesting
@visibleForTesting
void
notifyListeners
()
{
void
notifyListeners
()
{
assert
(
_debugAssertNotDisposed
());
assert
(
_debugAssertNotDisposed
());
if
(
_
listeners
!.
isEmpty
)
if
(
_
count
==
0
)
return
;
return
;
final
List
<
_ListenerEntry
>
localListeners
=
List
<
_ListenerEntry
>.
from
(
_listeners
!);
// To make sure that listeners removed during this iteration are not called,
// we set them to null, but we don't shrink the list right away.
// By doing this, we can continue to iterate on our list until it reaches
// the last listener added before the call to this method.
// To allow potential listeners to recursively call notifyListener, we track
// the number of times this method is called in _notificationCallStackDepth.
// Once every recursive iteration is finished (i.e. when _notificationCallStackDepth == 0),
// we can safely shrink our list so that it will only contain not null
// listeners.
for
(
final
_ListenerEntry
entry
in
localListeners
)
{
_notificationCallStackDepth
++;
final
int
end
=
_count
;
for
(
int
i
=
0
;
i
<
end
;
i
++)
{
try
{
try
{
if
(
entry
.
list
!=
null
)
_listeners
[
i
]?.
call
();
entry
.
listener
();
}
catch
(
exception
,
stack
)
{
}
catch
(
exception
,
stack
)
{
FlutterError
.
reportError
(
FlutterErrorDetails
(
FlutterError
.
reportError
(
FlutterErrorDetails
(
exception:
exception
,
exception:
exception
,
...
@@ -257,6 +321,44 @@ class ChangeNotifier implements Listenable {
...
@@ -257,6 +321,44 @@ class ChangeNotifier implements Listenable {
));
));
}
}
}
}
_notificationCallStackDepth
--;
if
(
_notificationCallStackDepth
==
0
&&
_reentrantlyRemovedListeners
>
0
)
{
// We really remove the listeners when all notifications are done.
final
int
newLength
=
_count
-
_reentrantlyRemovedListeners
;
if
(
newLength
*
2
<=
_listeners
.
length
)
{
// As in _removeAt, we only shrink the list when the real number of
// listeners is half the length of our list.
final
List
<
VoidCallback
?>
newListeners
=
List
<
VoidCallback
?>.
filled
(
newLength
,
null
);
int
newIndex
=
0
;
for
(
int
i
=
0
;
i
<
_count
;
i
++)
{
final
VoidCallback
?
listener
=
_listeners
[
i
];
if
(
listener
!=
null
)
{
newListeners
[
newIndex
++]
=
listener
;
}
}
_listeners
=
newListeners
;
}
else
{
// Otherwise we put all the null references at the end.
for
(
int
i
=
0
;
i
<
newLength
;
i
+=
1
)
{
if
(
_listeners
[
i
]
==
null
)
{
// We swap this item with the next not null item.
int
swapIndex
=
i
+
1
;
while
(
_listeners
[
swapIndex
]
==
null
){
swapIndex
+=
1
;
}
_listeners
[
i
]
=
_listeners
[
swapIndex
];
_listeners
[
swapIndex
]
=
null
;
}
}
}
_reentrantlyRemovedListeners
=
0
;
_count
=
newLength
;
}
}
}
}
}
...
...
packages/flutter/test/foundation/change_notifier_test.dart
View file @
10d5ec87
...
@@ -20,7 +20,9 @@ class HasListenersTester<T> extends ValueNotifier<T> {
...
@@ -20,7 +20,9 @@ class HasListenersTester<T> extends ValueNotifier<T> {
class
A
{
class
A
{
bool
result
=
false
;
bool
result
=
false
;
void
test
()
{
result
=
true
;
}
void
test
()
{
result
=
true
;
}
}
}
class
B
extends
A
with
ChangeNotifier
{
class
B
extends
A
with
ChangeNotifier
{
...
@@ -31,12 +33,36 @@ class B extends A with ChangeNotifier {
...
@@ -31,12 +33,36 @@ class B extends A with ChangeNotifier {
}
}
}
}
class
Counter
with
ChangeNotifier
{
int
get
value
=>
_value
;
int
_value
=
0
;
set
value
(
int
value
)
{
if
(
_value
!=
value
)
{
_value
=
value
;
notifyListeners
();
}
}
void
notify
()
{
notifyListeners
();
}
}
void
main
(
)
{
void
main
(
)
{
testWidgets
(
'ChangeNotifier'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'ChangeNotifier'
,
(
WidgetTester
tester
)
async
{
final
List
<
String
>
log
=
<
String
>[];
final
List
<
String
>
log
=
<
String
>[];
void
listener
()
{
log
.
add
(
'listener'
);
}
void
listener
()
{
void
listener1
()
{
log
.
add
(
'listener1'
);
}
log
.
add
(
'listener'
);
void
listener2
()
{
log
.
add
(
'listener2'
);
}
}
void
listener1
()
{
log
.
add
(
'listener1'
);
}
void
listener2
()
{
log
.
add
(
'listener2'
);
}
void
badListener
()
{
void
badListener
()
{
log
.
add
(
'badListener'
);
log
.
add
(
'badListener'
);
throw
ArgumentError
();
throw
ArgumentError
();
...
@@ -111,9 +137,18 @@ void main() {
...
@@ -111,9 +137,18 @@ void main() {
final
TestNotifier
test
=
TestNotifier
();
final
TestNotifier
test
=
TestNotifier
();
final
List
<
String
>
log
=
<
String
>[];
final
List
<
String
>
log
=
<
String
>[];
void
listener1
()
{
log
.
add
(
'listener1'
);
}
void
listener1
()
{
void
listener3
()
{
log
.
add
(
'listener3'
);
}
log
.
add
(
'listener1'
);
void
listener4
()
{
log
.
add
(
'listener4'
);
}
}
void
listener3
()
{
log
.
add
(
'listener3'
);
}
void
listener4
()
{
log
.
add
(
'listener4'
);
}
void
listener2
()
{
void
listener2
()
{
log
.
add
(
'listener2'
);
log
.
add
(
'listener2'
);
test
.
removeListener
(
listener1
);
test
.
removeListener
(
listener1
);
...
@@ -137,12 +172,19 @@ void main() {
...
@@ -137,12 +172,19 @@ void main() {
log
.
clear
();
log
.
clear
();
});
});
test
(
'During notifyListeners, a listener was added and removed immediately'
,
()
{
test
(
'During notifyListeners, a listener was added and removed immediately'
,
()
{
final
TestNotifier
source
=
TestNotifier
();
final
TestNotifier
source
=
TestNotifier
();
final
List
<
String
>
log
=
<
String
>[];
final
List
<
String
>
log
=
<
String
>[];
void
listener3
()
{
log
.
add
(
'listener3'
);
}
void
listener3
()
{
void
listener2
()
{
log
.
add
(
'listener2'
);
}
log
.
add
(
'listener3'
);
}
void
listener2
()
{
log
.
add
(
'listener2'
);
}
void
listener1
()
{
void
listener1
()
{
log
.
add
(
'listener1'
);
log
.
add
(
'listener1'
);
source
.
addListener
(
listener2
);
source
.
addListener
(
listener2
);
...
@@ -167,7 +209,10 @@ void main() {
...
@@ -167,7 +209,10 @@ void main() {
log
.
add
(
'selfRemovingListener'
);
log
.
add
(
'selfRemovingListener'
);
source
.
removeListener
(
selfRemovingListener
);
source
.
removeListener
(
selfRemovingListener
);
}
}
void
listener1
()
{
log
.
add
(
'listener1'
);
}
void
listener1
()
{
log
.
add
(
'listener1'
);
}
source
.
addListener
(
listener1
);
source
.
addListener
(
listener1
);
source
.
addListener
(
selfRemovingListener
);
source
.
addListener
(
selfRemovingListener
);
...
@@ -178,7 +223,9 @@ void main() {
...
@@ -178,7 +223,9 @@ void main() {
expect
(
log
,
<
String
>[
'listener1'
,
'selfRemovingListener'
,
'listener1'
]);
expect
(
log
,
<
String
>[
'listener1'
,
'selfRemovingListener'
,
'listener1'
]);
});
});
test
(
'If the first listener removes itself, notifyListeners still notify all listeners'
,
()
{
test
(
'If the first listener removes itself, notifyListeners still notify all listeners'
,
()
{
final
TestNotifier
source
=
TestNotifier
();
final
TestNotifier
source
=
TestNotifier
();
final
List
<
String
>
log
=
<
String
>[];
final
List
<
String
>
log
=
<
String
>[];
...
@@ -186,6 +233,7 @@ void main() {
...
@@ -186,6 +233,7 @@ void main() {
log
.
add
(
'selfRemovingListener'
);
log
.
add
(
'selfRemovingListener'
);
source
.
removeListener
(
selfRemovingListener
);
source
.
removeListener
(
selfRemovingListener
);
}
}
void
listener1
()
{
void
listener1
()
{
log
.
add
(
'listener1'
);
log
.
add
(
'listener1'
);
}
}
...
@@ -205,8 +253,13 @@ void main() {
...
@@ -205,8 +253,13 @@ void main() {
final
List
<
String
>
log
=
<
String
>[];
final
List
<
String
>
log
=
<
String
>[];
final
Listenable
merged
=
Listenable
.
merge
(<
Listenable
>[
source1
,
source2
]);
final
Listenable
merged
=
Listenable
.
merge
(<
Listenable
>[
source1
,
source2
]);
void
listener1
()
{
log
.
add
(
'listener1'
);
}
void
listener1
()
{
void
listener2
()
{
log
.
add
(
'listener2'
);
}
log
.
add
(
'listener1'
);
}
void
listener2
()
{
log
.
add
(
'listener2'
);
}
merged
.
addListener
(
listener1
);
merged
.
addListener
(
listener1
);
source1
.
notify
();
source1
.
notify
();
...
@@ -236,8 +289,11 @@ void main() {
...
@@ -236,8 +289,11 @@ void main() {
final
TestNotifier
source2
=
TestNotifier
();
final
TestNotifier
source2
=
TestNotifier
();
final
List
<
String
>
log
=
<
String
>[];
final
List
<
String
>
log
=
<
String
>[];
final
Listenable
merged
=
Listenable
.
merge
(<
Listenable
?>[
null
,
source1
,
null
,
source2
,
null
]);
final
Listenable
merged
=
void
listener
()
{
log
.
add
(
'listener'
);
}
Listenable
.
merge
(<
Listenable
?>[
null
,
source1
,
null
,
source2
,
null
]);
void
listener
()
{
log
.
add
(
'listener'
);
}
merged
.
addListener
(
listener
);
merged
.
addListener
(
listener
);
source1
.
notify
();
source1
.
notify
();
...
@@ -252,7 +308,9 @@ void main() {
...
@@ -252,7 +308,9 @@ void main() {
final
List
<
String
>
log
=
<
String
>[];
final
List
<
String
>
log
=
<
String
>[];
final
Listenable
merged
=
Listenable
.
merge
(<
Listenable
>[
source1
,
source2
]);
final
Listenable
merged
=
Listenable
.
merge
(<
Listenable
>[
source1
,
source2
]);
void
listener
()
{
log
.
add
(
'listener'
);
}
void
listener
()
{
log
.
add
(
'listener'
);
}
merged
.
addListener
(
listener
);
merged
.
addListener
(
listener
);
source1
.
notify
();
source1
.
notify
();
...
@@ -269,22 +327,32 @@ void main() {
...
@@ -269,22 +327,32 @@ void main() {
test
(
'Cannot use a disposed ChangeNotifier'
,
()
{
test
(
'Cannot use a disposed ChangeNotifier'
,
()
{
final
TestNotifier
source
=
TestNotifier
();
final
TestNotifier
source
=
TestNotifier
();
source
.
dispose
();
source
.
dispose
();
expect
(()
{
source
.
addListener
(()
{
});
},
throwsFlutterError
);
expect
(()
{
expect
(()
{
source
.
removeListener
(()
{
});
},
throwsFlutterError
);
source
.
addListener
(()
{});
expect
(()
{
source
.
dispose
();
},
throwsFlutterError
);
},
throwsFlutterError
);
expect
(()
{
source
.
notify
();
},
throwsFlutterError
);
expect
(()
{
source
.
removeListener
(()
{});
},
throwsFlutterError
);
expect
(()
{
source
.
dispose
();
},
throwsFlutterError
);
expect
(()
{
source
.
notify
();
},
throwsFlutterError
);
});
});
test
(
'Value notifier'
,
()
{
test
(
'Value notifier'
,
()
{
final
ValueNotifier
<
double
>
notifier
=
ValueNotifier
<
double
>(
2.0
);
final
ValueNotifier
<
double
>
notifier
=
ValueNotifier
<
double
>(
2.0
);
final
List
<
double
>
log
=
<
double
>[];
final
List
<
double
>
log
=
<
double
>[];
void
listener
()
{
log
.
add
(
notifier
.
value
);
}
void
listener
()
{
log
.
add
(
notifier
.
value
);
}
notifier
.
addListener
(
listener
);
notifier
.
addListener
(
listener
);
notifier
.
value
=
3.0
;
notifier
.
value
=
3.0
;
expect
(
log
,
equals
(<
double
>[
3.0
]));
expect
(
log
,
equals
(<
double
>[
3.0
]));
log
.
clear
();
log
.
clear
();
notifier
.
value
=
3.0
;
notifier
.
value
=
3.0
;
...
@@ -325,9 +393,10 @@ void main() {
...
@@ -325,9 +393,10 @@ void main() {
final
TestNotifier
source1
=
TestNotifier
();
final
TestNotifier
source1
=
TestNotifier
();
final
TestNotifier
source2
=
TestNotifier
();
final
TestNotifier
source2
=
TestNotifier
();
void
fakeListener
()
{
}
void
fakeListener
()
{}
final
Listenable
listenableUnderTest
=
Listenable
.
merge
(<
Listenable
>[
source1
,
source2
]);
final
Listenable
listenableUnderTest
=
Listenable
.
merge
(<
Listenable
>[
source1
,
source2
]);
expect
(
source1
.
isListenedTo
,
isFalse
);
expect
(
source1
.
isListenedTo
,
isFalse
);
expect
(
source2
.
isListenedTo
,
isFalse
);
expect
(
source2
.
isListenedTo
,
isFalse
);
listenableUnderTest
.
addListener
(
fakeListener
);
listenableUnderTest
.
addListener
(
fakeListener
);
...
@@ -339,12 +408,11 @@ void main() {
...
@@ -339,12 +408,11 @@ void main() {
expect
(
source2
.
isListenedTo
,
isFalse
);
expect
(
source2
.
isListenedTo
,
isFalse
);
});
});
test
(
'hasListeners'
,
()
{
test
(
'hasListeners'
,
()
{
final
HasListenersTester
<
bool
>
notifier
=
HasListenersTester
<
bool
>(
true
);
final
HasListenersTester
<
bool
>
notifier
=
HasListenersTester
<
bool
>(
true
);
expect
(
notifier
.
testHasListeners
,
isFalse
);
expect
(
notifier
.
testHasListeners
,
isFalse
);
void
test1
()
{
}
void
test1
()
{}
void
test2
()
{
}
void
test2
()
{}
notifier
.
addListener
(
test1
);
notifier
.
addListener
(
test1
);
expect
(
notifier
.
testHasListeners
,
isTrue
);
expect
(
notifier
.
testHasListeners
,
isTrue
);
notifier
.
addListener
(
test1
);
notifier
.
addListener
(
test1
);
...
@@ -388,12 +456,94 @@ void main() {
...
@@ -388,12 +456,94 @@ void main() {
}
}
expect
(
error
,
isNotNull
);
expect
(
error
,
isNotNull
);
expect
(
error
!,
isFlutterError
);
expect
(
error
!,
isFlutterError
);
expect
(
error
.
toStringDeep
(),
equalsIgnoringHashCodes
(
expect
(
'FlutterError
\n
'
error
.
toStringDeep
(),
' A TestNotifier was used after being disposed.
\n
'
equalsIgnoringHashCodes
(
'FlutterError
\n
'
' Once you have called dispose() on a TestNotifier, it can no
\n
'
' A TestNotifier was used after being disposed.
\n
'
' longer be used.
\n
'
' Once you have called dispose() on a TestNotifier, it can no
\n
'
));
' longer be used.
\n
'
));
});
test
(
'notifyListener can be called recursively'
,
()
{
final
Counter
counter
=
Counter
();
final
List
<
String
>
log
=
<
String
>[];
void
listener1
()
{
log
.
add
(
'listener1'
);
if
(
counter
.
value
<
0
)
{
counter
.
value
=
0
;
}
}
counter
.
addListener
(
listener1
);
counter
.
notify
();
expect
(
log
,
<
String
>[
'listener1'
]);
log
.
clear
();
counter
.
value
=
3
;
expect
(
log
,
<
String
>[
'listener1'
]);
log
.
clear
();
counter
.
value
=
-
2
;
expect
(
log
,
<
String
>[
'listener1'
,
'listener1'
]);
log
.
clear
();
});
});
test
(
'Remove Listeners while notifying on a list which will not resize'
,
()
{
final
TestNotifier
test
=
TestNotifier
();
final
List
<
String
>
log
=
<
String
>[];
final
List
<
VoidCallback
>
listeners
=
<
VoidCallback
>[];
void
autoRemove
()
{
// We remove 4 listeners.
// We will end up with (13-4 = 9) listeners.
test
.
removeListener
(
listeners
[
1
]);
test
.
removeListener
(
listeners
[
3
]);
test
.
removeListener
(
listeners
[
4
]);
test
.
removeListener
(
autoRemove
);
}
test
.
addListener
(
autoRemove
);
// We add 12 more listeners.
for
(
int
i
=
0
;
i
<
12
;
i
++)
{
void
listener
()
{
log
.
add
(
'listener
$i
'
);
}
listeners
.
add
(
listener
);
test
.
addListener
(
listener
);
}
final
List
<
int
>
remainingListenerIndexes
=
<
int
>[
0
,
2
,
5
,
6
,
7
,
8
,
9
,
10
,
11
];
final
List
<
String
>
expectedLog
=
remainingListenerIndexes
.
map
((
int
i
)
=>
'listener
$i
'
).
toList
();
test
.
notify
();
expect
(
log
,
expectedLog
);
log
.
clear
();
// We expect to have the same result after the removal of previous listeners.
test
.
notify
();
expect
(
log
,
expectedLog
);
// We remove all other listeners.
for
(
int
i
=
0
;
i
<
remainingListenerIndexes
.
length
;
i
++)
{
test
.
removeListener
(
listeners
[
remainingListenerIndexes
[
i
]]);
}
log
.
clear
();
test
.
notify
();
expect
(
log
,
<
String
>[]);
});
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment