Skip to content

Commit f6c3ee3

Browse files
authored
Add ShortcutsRegistry (#103456)
This adds a ShortcutsRegistry for ShortcutActivator to Intent mappings that can be modified from its descendants. This is so that descendants can make shortcuts dynamically available to a larger portion of the app than just their descendants. This is a precursor needed by the new MenuBar, for instance, so that the menu bar itself can be placed where it likes, but the shortcuts it defines can be in effect for most, if not all, of the UI surface in the app. For example, the "Ctrl-Q" quit binding would need to work even if the focused widget wasn't a child of the MenuBar. This just provides the shortcut to intent mapping, the actions activated by the intent are described in the context where they make sense. For example, defining a "Ctrl-C" shortcut mapped to a "CopyIntent" should perform different functions if it happens while a TextField has focus vs when a drawing has focus, so those different areas would need to define different actions mapped to "CopyIntent". A hypothetical "QuitIntent" would probably be active for the entire app, so would be mapped in an Actions widget near the top of the hierarchy.
1 parent d2ba83d commit f6c3ee3

File tree

4 files changed

+829
-145
lines changed

4 files changed

+829
-145
lines changed

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

+22-5
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,24 @@ class ChangeNotifier implements Listenable {
119119
int _reentrantlyRemovedListeners = 0;
120120
bool _debugDisposed = false;
121121

122-
bool _debugAssertNotDisposed() {
122+
/// Used by subclasses to assert that the [ChangeNotifier] has not yet been
123+
/// disposed.
124+
///
125+
/// {@tool snippet}
126+
/// The `debugAssertNotDisposed` function should only be called inside of an
127+
/// assert, as in this example.
128+
///
129+
/// ```dart
130+
/// class MyNotifier with ChangeNotifier {
131+
/// void doUpdate() {
132+
/// assert(debugAssertNotDisposed());
133+
/// // ...
134+
/// }
135+
/// }
136+
/// ```
137+
/// {@end-tool}
138+
@protected
139+
bool debugAssertNotDisposed() {
123140
assert(() {
124141
if (_debugDisposed) {
125142
throw FlutterError(
@@ -149,7 +166,7 @@ class ChangeNotifier implements Listenable {
149166
/// so, stopping that same work.
150167
@protected
151168
bool get hasListeners {
152-
assert(_debugAssertNotDisposed());
169+
assert(debugAssertNotDisposed());
153170
return _count > 0;
154171
}
155172

@@ -181,7 +198,7 @@ class ChangeNotifier implements Listenable {
181198
/// the list of closures that are notified when the object changes.
182199
@override
183200
void addListener(VoidCallback listener) {
184-
assert(_debugAssertNotDisposed());
201+
assert(debugAssertNotDisposed());
185202
if (_count == _listeners.length) {
186203
if (_count == 0) {
187204
_listeners = List<VoidCallback?>.filled(1, null);
@@ -273,7 +290,7 @@ class ChangeNotifier implements Listenable {
273290
/// This method should only be called by the object's owner.
274291
@mustCallSuper
275292
void dispose() {
276-
assert(_debugAssertNotDisposed());
293+
assert(debugAssertNotDisposed());
277294
assert(() {
278295
_debugDisposed = true;
279296
return true;
@@ -301,7 +318,7 @@ class ChangeNotifier implements Listenable {
301318
@visibleForTesting
302319
@pragma('vm:notify-debugger-on-exception')
303320
void notifyListeners() {
304-
assert(_debugAssertNotDisposed());
321+
assert(debugAssertNotDisposed());
305322
if (_count == 0)
306323
return;
307324

packages/flutter/lib/src/widgets/restoration.dart

+5-18
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ abstract class RestorableProperty<T> extends ChangeNotifier {
473473

474474
@override
475475
void dispose() {
476-
assert(_debugAssertNotDisposed());
476+
assert(debugAssertNotDisposed()); // FYI, This uses ChangeNotifier's _debugDisposed, not _disposed.
477477
_owner?._unregister(this);
478478
super.dispose();
479479
_disposed = true;
@@ -483,14 +483,14 @@ abstract class RestorableProperty<T> extends ChangeNotifier {
483483
String? _restorationId;
484484
RestorationMixin? _owner;
485485
void _register(String restorationId, RestorationMixin owner) {
486-
assert(_debugAssertNotDisposed());
486+
assert(debugAssertNotDisposed());
487487
assert(restorationId != null);
488488
assert(owner != null);
489489
_restorationId = restorationId;
490490
_owner = owner;
491491
}
492492
void _unregister() {
493-
assert(_debugAssertNotDisposed());
493+
assert(debugAssertNotDisposed());
494494
assert(_restorationId != null);
495495
assert(_owner != null);
496496
_restorationId = null;
@@ -503,29 +503,16 @@ abstract class RestorableProperty<T> extends ChangeNotifier {
503503
@protected
504504
State get state {
505505
assert(isRegistered);
506-
assert(_debugAssertNotDisposed());
506+
assert(debugAssertNotDisposed());
507507
return _owner!;
508508
}
509509

510510
/// Whether this property is currently registered with a [RestorationMixin].
511511
@protected
512512
bool get isRegistered {
513-
assert(_debugAssertNotDisposed());
513+
assert(debugAssertNotDisposed());
514514
return _restorationId != null;
515515
}
516-
517-
bool _debugAssertNotDisposed() {
518-
assert(() {
519-
if (_disposed) {
520-
throw FlutterError(
521-
'A $runtimeType was used after being disposed.\n'
522-
'Once you have called dispose() on a $runtimeType, it can no longer be used.',
523-
);
524-
}
525-
return true;
526-
}());
527-
return true;
528-
}
529516
}
530517

531518
/// Manages the restoration data for a [State] object of a [StatefulWidget].

0 commit comments

Comments
 (0)