mirror of
https://github.com/GitJournal/GitJournal.git
synced 2025-06-26 08:36:50 +08:00
null safety++
This commit is contained in:
@ -1,19 +1,27 @@
|
|||||||
// @dart=2.9
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// A duplicate of the ChangeNotifier class which has the dispose method
|
/// A duplicate of the ChangeNotifier class which has the dispose method
|
||||||
/// renamed to disposeListeners. This is done so that it can be used
|
/// renamed to disposeListenables. This is done so that it can be used
|
||||||
/// as a mixin with a State class which also has a dispose method
|
/// as a mixin with a State class which also has a dispose method
|
||||||
|
|
||||||
|
class _ListenerEntry extends LinkedListEntry<_ListenerEntry> {
|
||||||
|
_ListenerEntry(this.listener);
|
||||||
|
final VoidCallback listener;
|
||||||
|
}
|
||||||
|
|
||||||
class DisposableChangeNotifier implements Listenable {
|
class DisposableChangeNotifier implements Listenable {
|
||||||
ObserverList<VoidCallback> _listeners = ObserverList<VoidCallback>();
|
LinkedList<_ListenerEntry>? _listeners = LinkedList<_ListenerEntry>();
|
||||||
|
|
||||||
bool _debugAssertNotDisposed() {
|
bool _debugAssertNotDisposed() {
|
||||||
assert(() {
|
assert(() {
|
||||||
if (_listeners == null) {
|
if (_listeners == null) {
|
||||||
throw FlutterError('A $runtimeType was used after being disposed.\n'
|
throw FlutterError(
|
||||||
'Once you have called dispose() on a $runtimeType, it can no longer be used.');
|
'A $runtimeType was used after being disposed.\n'
|
||||||
|
'Once you have called dispose() on a $runtimeType, it can no longer be used.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}());
|
}());
|
||||||
@ -38,16 +46,39 @@ class DisposableChangeNotifier implements Listenable {
|
|||||||
@protected
|
@protected
|
||||||
bool get hasListeners {
|
bool get hasListeners {
|
||||||
assert(_debugAssertNotDisposed());
|
assert(_debugAssertNotDisposed());
|
||||||
return _listeners.isNotEmpty;
|
return _listeners!.isNotEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a closure to be called when the object changes.
|
/// Register a closure to be called when the object changes.
|
||||||
///
|
///
|
||||||
|
/// If the given closure is already registered, an additional instance is
|
||||||
|
/// added, and must be removed the same number of times it is added before it
|
||||||
|
/// will stop being called.
|
||||||
|
///
|
||||||
/// This method must not be called after [dispose] has been called.
|
/// This method must not be called after [dispose] has been called.
|
||||||
|
///
|
||||||
|
/// {@template flutter.foundation.ChangeNotifier.addListener}
|
||||||
|
/// If a listener is added twice, and is removed once during an iteration
|
||||||
|
/// (e.g. in response to a notification), it will still be called again. If,
|
||||||
|
/// on the other hand, it is removed as many times as it was registered, then
|
||||||
|
/// it will no longer be called. This odd behavior is the result of the
|
||||||
|
/// [ChangeNotifier] not being able to determine which listener is being
|
||||||
|
/// removed, since they are identical, therefore it will conservatively still
|
||||||
|
/// call all the listeners when it knows that any are still registered.
|
||||||
|
///
|
||||||
|
/// This surprising behavior can be unexpectedly observed when registering a
|
||||||
|
/// listener on two separate objects which are both forwarding all
|
||||||
|
/// registrations to a common upstream object.
|
||||||
|
/// {@endtemplate}
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [removeListener], which removes a previously registered closure from
|
||||||
|
/// the list of closures that are notified when the object changes.
|
||||||
@override
|
@override
|
||||||
void addListener(VoidCallback listener) {
|
void addListener(VoidCallback listener) {
|
||||||
assert(_debugAssertNotDisposed());
|
assert(_debugAssertNotDisposed());
|
||||||
_listeners.add(listener);
|
_listeners!.add(_ListenerEntry(listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove a previously registered closure from the list of closures that are
|
/// Remove a previously registered closure from the list of closures that are
|
||||||
@ -57,22 +88,21 @@ class DisposableChangeNotifier implements Listenable {
|
|||||||
///
|
///
|
||||||
/// This method must not be called after [dispose] has been called.
|
/// This method must not be called after [dispose] has been called.
|
||||||
///
|
///
|
||||||
/// If a listener had been added twice, and is removed once during an
|
/// {@macro flutter.foundation.ChangeNotifier.addListener}
|
||||||
/// iteration (i.e. in response to a notification), it will still be called
|
|
||||||
/// again. If, on the other hand, it is removed as many times as it was
|
|
||||||
/// registered, then it will no longer be called. This odd behavior is the
|
|
||||||
/// result of the [ChangeNotifier] not being able to determine which listener
|
|
||||||
/// is being removed, since they are identical, and therefore conservatively
|
|
||||||
/// still calling all the listeners when it knows that any are still
|
|
||||||
/// registered.
|
|
||||||
///
|
///
|
||||||
/// This surprising behavior can be unexpectedly observed when registering a
|
/// See also:
|
||||||
/// listener on two separate objects which are both forwarding all
|
///
|
||||||
/// registrations to a common upstream object.
|
/// * [addListener], which registers a closure to be called when the object
|
||||||
|
/// changes.
|
||||||
@override
|
@override
|
||||||
void removeListener(VoidCallback listener) {
|
void removeListener(VoidCallback listener) {
|
||||||
assert(_debugAssertNotDisposed());
|
assert(_debugAssertNotDisposed());
|
||||||
_listeners.remove(listener);
|
for (final _ListenerEntry entry in _listeners!) {
|
||||||
|
if (entry.listener == listener) {
|
||||||
|
entry.unlink();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Discards any resources used by the object. After this is called, the
|
/// Discards any resources used by the object. After this is called, the
|
||||||
@ -90,46 +120,45 @@ class DisposableChangeNotifier implements Listenable {
|
|||||||
/// Call all the registered listeners.
|
/// Call all the registered listeners.
|
||||||
///
|
///
|
||||||
/// Call this method whenever the object changes, to notify any clients the
|
/// Call this method whenever the object changes, to notify any clients the
|
||||||
/// object may have. Listeners that are added during this iteration will not
|
/// object may have changed. Listeners that are added during this iteration
|
||||||
/// be visited. Listeners that are removed during this iteration will not be
|
/// will not be visited. Listeners that are removed during this iteration will
|
||||||
/// visited after they are removed.
|
/// not be visited after they are removed.
|
||||||
///
|
///
|
||||||
/// Exceptions thrown by listeners will be caught and reported using
|
/// Exceptions thrown by listeners will be caught and reported using
|
||||||
/// [FlutterError.reportError].
|
/// [FlutterError.reportError].
|
||||||
///
|
///
|
||||||
/// This method must not be called after [dispose] has been called.
|
/// This method must not be called after [dispose] has been called.
|
||||||
///
|
///
|
||||||
/// Surprising behavior can result when reentrantly removing a listener (i.e.
|
/// Surprising behavior can result when reentrantly removing a listener (e.g.
|
||||||
/// in response to a notification) that has been registered multiple times.
|
/// in response to a notification) that has been registered multiple times.
|
||||||
/// See the discussion at [removeListener].
|
/// See the discussion at [removeListener].
|
||||||
@protected
|
@protected
|
||||||
@visibleForTesting
|
@visibleForTesting
|
||||||
void notifyListeners() {
|
void notifyListeners() {
|
||||||
assert(_debugAssertNotDisposed());
|
assert(_debugAssertNotDisposed());
|
||||||
if (_listeners != null) {
|
if (_listeners!.isEmpty) return;
|
||||||
final List<VoidCallback> localListeners =
|
|
||||||
List<VoidCallback>.from(_listeners);
|
final List<_ListenerEntry> localListeners =
|
||||||
for (final VoidCallback listener in localListeners) {
|
List<_ListenerEntry>.from(_listeners!);
|
||||||
try {
|
|
||||||
if (_listeners.contains(listener)) {
|
for (final _ListenerEntry entry in localListeners) {
|
||||||
listener();
|
try {
|
||||||
}
|
if (entry.list != null) entry.listener();
|
||||||
} catch (exception, stack) {
|
} catch (exception, stack) {
|
||||||
FlutterError.reportError(FlutterErrorDetails(
|
FlutterError.reportError(FlutterErrorDetails(
|
||||||
exception: exception,
|
exception: exception,
|
||||||
stack: stack,
|
stack: stack,
|
||||||
library: 'foundation library',
|
library: 'foundation library',
|
||||||
context: ErrorDescription(
|
context: ErrorDescription(
|
||||||
'while dispatching notifications for $runtimeType'),
|
'while dispatching notifications for $runtimeType'),
|
||||||
informationCollector: () sync* {
|
informationCollector: () sync* {
|
||||||
yield DiagnosticsProperty<DisposableChangeNotifier>(
|
yield DiagnosticsProperty<DisposableChangeNotifier>(
|
||||||
'The $runtimeType sending notification was',
|
'The $runtimeType sending notification was',
|
||||||
this,
|
this,
|
||||||
style: DiagnosticsTreeStyle.errorProperty,
|
style: DiagnosticsTreeStyle.errorProperty,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// @dart=2.9
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:gitjournal/core/notes_folder_fs.dart';
|
import 'package:gitjournal/core/notes_folder_fs.dart';
|
||||||
@ -14,9 +12,9 @@ class FolderTreeView extends StatefulWidget {
|
|||||||
final FolderSelectedCallback onFolderEntered;
|
final FolderSelectedCallback onFolderEntered;
|
||||||
|
|
||||||
FolderTreeView({
|
FolderTreeView({
|
||||||
Key key,
|
Key? key,
|
||||||
@required this.rootFolder,
|
required this.rootFolder,
|
||||||
@required this.onFolderEntered,
|
required this.onFolderEntered,
|
||||||
this.onFolderSelected = _doNothing,
|
this.onFolderSelected = _doNothing,
|
||||||
this.onFolderUnselected = _doNothing,
|
this.onFolderUnselected = _doNothing,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@ -29,7 +27,7 @@ class FolderTreeView extends StatefulWidget {
|
|||||||
|
|
||||||
class FolderTreeViewState extends State<FolderTreeView> {
|
class FolderTreeViewState extends State<FolderTreeView> {
|
||||||
bool inSelectionMode = false;
|
bool inSelectionMode = false;
|
||||||
NotesFolderFS selectedFolder;
|
NotesFolderFS? selectedFolder;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -73,13 +71,13 @@ class FolderTile extends StatefulWidget {
|
|||||||
final NotesFolderFS folder;
|
final NotesFolderFS folder;
|
||||||
final FolderSelectedCallback onTap;
|
final FolderSelectedCallback onTap;
|
||||||
final FolderSelectedCallback onLongPress;
|
final FolderSelectedCallback onLongPress;
|
||||||
final NotesFolderFS selectedFolder;
|
final NotesFolderFS? selectedFolder;
|
||||||
|
|
||||||
FolderTile({
|
FolderTile({
|
||||||
@required this.folder,
|
required this.folder,
|
||||||
@required this.onTap,
|
required this.onTap,
|
||||||
@required this.onLongPress,
|
required this.onLongPress,
|
||||||
@required this.selectedFolder,
|
required this.selectedFolder,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -127,7 +125,7 @@ class FolderTileState extends State<FolderTile> {
|
|||||||
|
|
||||||
var publicName = folder.publicName;
|
var publicName = folder.publicName;
|
||||||
if (folder.parent != null) {
|
if (folder.parent != null) {
|
||||||
publicName = publicName.substring(folder.parent.pathSpec().length);
|
publicName = publicName.substring(folder.parent!.pathSpec().length);
|
||||||
if (publicName.startsWith('/')) {
|
if (publicName.startsWith('/')) {
|
||||||
publicName = publicName.substring(1);
|
publicName = publicName.substring(1);
|
||||||
}
|
}
|
||||||
@ -167,7 +165,7 @@ class FolderTileState extends State<FolderTile> {
|
|||||||
var children = <FolderTile>[];
|
var children = <FolderTile>[];
|
||||||
widget.folder.subFolders.forEach((folder) {
|
widget.folder.subFolders.forEach((folder) {
|
||||||
children.add(FolderTile(
|
children.add(FolderTile(
|
||||||
folder: folder,
|
folder: folder as NotesFolderFS,
|
||||||
onTap: widget.onTap,
|
onTap: widget.onTap,
|
||||||
onLongPress: widget.onLongPress,
|
onLongPress: widget.onLongPress,
|
||||||
selectedFolder: widget.selectedFolder,
|
selectedFolder: widget.selectedFolder,
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
// @dart=2.9
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copyright 2020-2021 Vishesh Handa <me@vhanda.in>
|
Copyright 2020-2021 Vishesh Handa <me@vhanda.in>
|
||||||
|
|
||||||
@ -85,7 +83,7 @@ class NoteEditorSelector extends StatelessWidget {
|
|||||||
var selected = et == currentEditor;
|
var selected = et == currentEditor;
|
||||||
var theme = Theme.of(context);
|
var theme = Theme.of(context);
|
||||||
var listTileTheme = ListTileTheme.of(context);
|
var listTileTheme = ListTileTheme.of(context);
|
||||||
var textStyle = theme.textTheme.bodyText2.copyWith(
|
var textStyle = theme.textTheme.bodyText2!.copyWith(
|
||||||
color: selected ? theme.primaryColor : listTileTheme.textColor,
|
color: selected ? theme.primaryColor : listTileTheme.textColor,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user