1
|
|
// Copyright 2020 J-P Nurmi
|
2
|
|
//
|
3
|
|
// ItemSelectionNotifier is based on Flutter's ChangeNotifier:
|
4
|
|
// https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/foundation/change_notifier.dart
|
5
|
|
//
|
6
|
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
7
|
|
//
|
8
|
|
// Redistribution and use in source and binary forms, with or without modification,
|
9
|
|
// are permitted provided that the following conditions are met:
|
10
|
|
//
|
11
|
|
// * Redistributions of source code must retain the above copyright
|
12
|
|
// notice, this list of conditions and the following disclaimer.
|
13
|
|
// * Redistributions in binary form must reproduce the above
|
14
|
|
// copyright notice, this list of conditions and the following
|
15
|
|
// disclaimer in the documentation and/or other materials provided
|
16
|
|
// with the distribution.
|
17
|
|
// * Neither the name of Google Inc. nor the names of its
|
18
|
|
// contributors may be used to endorse or promote products derived
|
19
|
|
// from this software without specific prior written permission.
|
20
|
|
//
|
21
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
22
|
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
23
|
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
24
|
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
25
|
|
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
26
|
|
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
27
|
|
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
28
|
|
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
29
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
30
|
|
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
31
|
|
|
32
|
|
import 'dart:collection';
|
33
|
|
|
34
|
|
import 'package:flutter/foundation.dart';
|
35
|
|
|
36
|
|
import 'item_selection_types.dart';
|
37
|
|
|
38
|
|
abstract class ItemSelectionNotifier {
|
39
|
|
/// Adds a [listener] callback for selection changes at any index.
|
40
|
1
|
void addListener(ItemSelectionChangeCallback listener) {
|
41
|
1
|
assert(_debugAssertNotDisposed());
|
42
|
1
|
_listeners.add(listener);
|
43
|
|
}
|
44
|
|
|
45
|
|
/// Removes a [listener] callback for selection changes at any index.
|
46
|
1
|
void removeListener(ItemSelectionChangeCallback listener) {
|
47
|
1
|
assert(_debugAssertNotDisposed());
|
48
|
1
|
_listeners.remove(listener);
|
49
|
|
}
|
50
|
|
|
51
|
|
/// Adds a [listener] callback for selection changes at a specific [index] only.
|
52
|
1
|
void addIndexListener(int index, ItemSelectionChangeCallback listener) {
|
53
|
1
|
assert(_debugAssertNotDisposed());
|
54
|
1
|
if (!_indexListeners.containsKey(index)) {
|
55
|
1
|
_indexListeners[index] = ObserverList<ItemSelectionChangeCallback>();
|
56
|
|
}
|
57
|
1
|
_indexListeners[index].add(listener);
|
58
|
|
}
|
59
|
|
|
60
|
|
/// Removes a [listener] callback for selection changes at a specific [index] only.
|
61
|
1
|
void removeIndexListener(int index, ItemSelectionChangeCallback listener) {
|
62
|
1
|
assert(_debugAssertNotDisposed());
|
63
|
1
|
if (_indexListeners.containsKey(index)) {
|
64
|
1
|
_indexListeners[index].remove(listener);
|
65
|
|
}
|
66
|
|
}
|
67
|
|
|
68
|
|
/// Notifies all the registered listeners.
|
69
|
1
|
@protected
|
70
|
|
void notifyListeners(int index, bool selected) {
|
71
|
1
|
assert(_debugAssertNotDisposed());
|
72
|
1
|
if (_listeners != null) {
|
73
|
1
|
final allListeners = List<ItemSelectionChangeCallback>.from(
|
74
|
1
|
[..._listeners, ...?_indexListeners[index]]);
|
75
|
1
|
for (final listener in allListeners) {
|
76
|
|
try {
|
77
|
1
|
listener(index, selected);
|
78
|
|
} catch (exception, stack) {
|
79
|
0
|
FlutterError.reportError(
|
80
|
0
|
FlutterErrorDetails(
|
81
|
|
exception: exception,
|
82
|
|
stack: stack,
|
83
|
|
library: 'item_selector',
|
84
|
0
|
context: ErrorDescription(
|
85
|
0
|
'while dispatching notifications for $runtimeType'),
|
86
|
|
informationCollector: () sync* {
|
87
|
|
yield DiagnosticsProperty<ItemSelectionNotifier>(
|
88
|
|
'The $runtimeType sending notification was',
|
89
|
|
this,
|
90
|
|
style: DiagnosticsTreeStyle.errorProperty,
|
91
|
|
);
|
92
|
|
},
|
93
|
|
),
|
94
|
|
);
|
95
|
|
}
|
96
|
|
}
|
97
|
|
}
|
98
|
|
}
|
99
|
|
|
100
|
|
/// Discards the listener callbacks. After this method is called, the notifier
|
101
|
|
/// object is no longer in a usable state and should be discarded (calls to
|
102
|
|
/// [addListener] and [removeListener] will throw after the object is
|
103
|
|
/// disposed).
|
104
|
|
///
|
105
|
|
/// This method should only be called by the object's owner.
|
106
|
1
|
@mustCallSuper
|
107
|
|
void dispose() {
|
108
|
1
|
assert(_debugAssertNotDisposed());
|
109
|
1
|
_indexListeners = null;
|
110
|
1
|
_listeners = null;
|
111
|
|
}
|
112
|
|
|
113
|
1
|
bool _debugAssertNotDisposed() {
|
114
|
1
|
assert(() {
|
115
|
1
|
if (_listeners == null || _indexListeners == null) {
|
116
|
1
|
throw FlutterError('A $runtimeType was used after being disposed.\n'
|
117
|
1
|
'Once you have called dispose() on a $runtimeType, it can no longer be used.');
|
118
|
|
}
|
119
|
|
return true;
|
120
|
1
|
}());
|
121
|
|
return true;
|
122
|
|
}
|
123
|
|
|
124
|
|
ObserverList<ItemSelectionChangeCallback> _listeners =
|
125
|
|
ObserverList<ItemSelectionChangeCallback>();
|
126
|
|
HashMap<int, ObserverList<ItemSelectionChangeCallback>> _indexListeners =
|
127
|
|
HashMap<int, ObserverList<ItemSelectionChangeCallback>>();
|
128
|
|
}
|