Skip to content

Commit

Permalink
5.6.1
Browse files Browse the repository at this point in the history
  • Loading branch information
Drawner committed Nov 15, 2024
1 parent 3033eee commit be6fe1f
Show file tree
Hide file tree
Showing 15 changed files with 202 additions and 74 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@

## 5.6.1
November 15,2024
- Removed mixin, ChangeNotifier, from class StateXController to be accessed indirectly
using a new mixin, ImplNotifyListenersChangeNotifierMixin, that contains it as a property.
This is because ChangeNotifier has no, super.dispose(), in its dispose() function.

## 5.6.0+1
November 15, 2024
November 14, 2024
- Introduce setBuilder() in every Controller. Will rebuild with every setState() call.
- Replacing stateSet(WidgetBuilder? builder) with the name, setBuilder in class, StateX and AppStateX
- class StateXController now includes mixin, ChangeNotifier and ListenableWidgetBuilderMixin
Expand Down
26 changes: 26 additions & 0 deletions lib/part01_statex.dart
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,30 @@ abstract class StateX<T extends StatefulWidget> extends State<StatefulWidget>
}());
}

// /// Notify all the [Listenable] objects that are the State object's controllers.
// void notifyListeners() {
//
// /// No 'setState()' functions are allowed to fully function at this point.
// _setStateAllowed = false;
//
// for (final con in controllerList) {
// con.notifyListeners();
// }
//
// _setStateAllowed = true;
//
// // The InheritedWidget will dictate if widgets are rebuilt.
// _setStateRequested = false;
//
// assert(() {
// if (_printEvents) {
// debugPrint('============ Event: notifyListeners() in $this');
// }
// return true;
// }());
// }


/// This method is also called immediately after [initState].
/// Otherwise called only if this [State] object's Widget
/// is a dependency of [InheritedWidget].
Expand Down Expand Up @@ -1361,6 +1385,8 @@ abstract class StateX<T extends StatefulWidget> extends State<StatefulWidget>
/// Refresh the interface by 'rebuilding' the Widget Tree
/// Call the State object's setState() function.
super.setState(fn);
// /// Call any [Listenable] objects.
// notifyListeners();
}
_setStateAllowed = true;
} else {
Expand Down
4 changes: 2 additions & 2 deletions lib/part06_inherited_widget_state_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ mixin InheritedWidgetStateMixin on State {
/// This 'widget function' will be called again.
Widget setBuilder(WidgetBuilder? builder) => stateSet(builder);
/// Called when the State's InheritedWidget is called again
/// This 'widget function' will be called again.
/// This 'widget builder' will be called again.
//@Deprecated('Use statBuilder() instead.')
Widget stateSet(WidgetBuilder? builder) {
builder ??= (_) => const SizedBox.shrink();
return useInherited && this is StateX
? _SetStateXWidget(stateX: this as StateX, builder: builder)
? StateDependentWidget(stateMixin: this as StateX, builder: builder)
: builder(context);
}

Expand Down
28 changes: 15 additions & 13 deletions lib/part08_app_statex.dart
Original file line number Diff line number Diff line change
Expand Up @@ -331,15 +331,16 @@ abstract class AppStateX<T extends StatefulWidget> extends StateX<T>
/// Called when the State's InheritedWidget is called again
/// This 'widget function' will be called again.
@override
Widget setBuilder(WidgetBuilder? widgetFunc) => stateSet(widgetFunc);
Widget setBuilder(WidgetBuilder? builder) => stateSet(builder);

/// Called when the State's InheritedWidget is called again
/// This 'widget function' will be called again.
@override
// @Deprecated('Use stateBuilder() instead.')
Widget stateSet(WidgetBuilder? widgetFunc) {
widgetFunc ??=
Widget stateSet(WidgetBuilder? builder) {
builder ??=
(_) => const SizedBox.shrink(); // Display 'nothing' if not provided
return _SetStateXWidget(stateX: this, builder: widgetFunc);
return StateDependentWidget(stateMixin: this, builder: builder);
}

/// Catch any errors in the App
Expand Down Expand Up @@ -560,19 +561,20 @@ abstract class AppStateX<T extends StatefulWidget> extends StateX<T>
}
}

/// Supply a widget to depend upon a StateX's InheritedWidget
class _SetStateXWidget extends StatelessWidget {
/// Supply a widget from a widget builder and possibly depend on InheritedWidget
class StateDependentWidget extends StatelessWidget {
///
const _SetStateXWidget({
required this.stateX,
required this.builder,
const StateDependentWidget({
super.key,
this.stateMixin,
this.builder,
});
final StateX stateX;
final WidgetBuilder builder;
final InheritedWidgetStateMixin? stateMixin;
final WidgetBuilder? builder;
@override
Widget build(BuildContext context) {
stateX.dependOnInheritedWidget(context);
return builder(context);
stateMixin?.dependOnInheritedWidget(context);
return builder?.call(context) ?? const SizedBox.shrink();
}
}

Expand Down
21 changes: 11 additions & 10 deletions lib/part13_statex_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,8 @@ part of 'state_extended.dart';
/// {@category State Object Controller}
class StateXController
with
ChangeNotifier,
SetStateMixin,
ListenableWidgetBuilderMixin,
ImplNotifyListenersChangeNotifierMixin,
StateListener,
RootState,
AsyncOps {
Expand All @@ -32,6 +31,8 @@ class StateXController
@override
@mustCallSuper
void dispose() {
// Call the dispose of the implementation of Change Notifier
disposeChangeNotifier();
/// Controllers state property is now null from deactivate() function
/// Always call 'initializing' routines in initState() and activate()
super.dispose();
Expand All @@ -54,14 +55,6 @@ class StateXController
/// The current StateX object.
StateX? get state => _stateX;

/// Call a State object's setState()
/// and notify any listeners
@override
void setState(VoidCallback fn) {
super.setState(fn);
notifyListeners();
}

/// Link a widget to an InheritedWidget
bool dependOnInheritedWidget(BuildContext? context) =>
_stateX?.dependOnInheritedWidget(context) ?? false;
Expand All @@ -70,6 +63,14 @@ class StateXController
/// Rebuild the InheritedWidget of the 'closes' InheritedStateX object if any.
bool notifyClients() => _stateX?.notifyClients() ?? false;

/// Call a State object's setState()
/// and notify any listeners
@override
void setState(VoidCallback fn) {
super.setState(fn);
notifyListeners();
}

/// The 'Change' event has already been called in a previous State object
bool get didCallChangeEvent {
bool change = false;
Expand Down
99 changes: 99 additions & 0 deletions lib/part14_change_notifier_class.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2024 Andrious Solutions Ltd. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

part of 'state_extended.dart';

/// Allows you to use a ChangeNotifier as a Mixin
///
/// setBuilder(WidgetBuilder? builder) rebuilds with every setState() call.
///
/// *IMPORTANT* Important to call the ChangeNotifier's dispose() function.
///
/// dartdoc:
/// {@category StateX class}
/// {@category State Object Controller}
mixin ImplNotifyListenersChangeNotifierMixin {
//

// Initialize the Change Notifier
void initChangeNotifier() {
implChangeNotifier ??= ImplNotifyListenersChangeNotifier();
}

/// Don't forget to call dispose() function!
void disposeChangeNotifier() {
implChangeNotifier?.dispose();
implChangeNotifier = null;
}

// Implementation of the ChangeNotifier
ImplNotifyListenersChangeNotifier? implChangeNotifier;

/// Returns a widget from builder assuming the current object is a [Listenable]
/// const SizedBox.shrink() otherwise
Widget setBuilder(MaybeBuildWidgetType? builder) {
// Ensure the ChangeNotifier is initialized
initChangeNotifier();

if (builder != null) {
_widgetBuilderUsed = true;
}
return ListenableWidgetBuilder(
listenable: implChangeNotifier,
builder: builder,
);
}

/// A flag. Noting if the function above is ever used.
bool get setBuilderUsed => _widgetBuilderUsed;
bool _widgetBuilderUsed = false;

/// Whether any listeners are currently registered.
bool get hasListeners => implChangeNotifier?.hasListeners ?? false;

/// Call all the registered listeners.
bool notifyListeners() {
final notify = implChangeNotifier != null;
if (notify) {
implChangeNotifier?.notifyListeners();
}
return notify;
}

/// Register a closure to be called when the object changes.
bool addListener(VoidCallback listener) {
final add = implChangeNotifier != null;
if (add) {
implChangeNotifier?.addListener(listener);
}
return add;
}

/// Remove a previously registered closure from the list of closures that are
/// notified when the object changes.
bool removeListener(VoidCallback listener) {
final remove = implChangeNotifier != null;
if (remove) {
implChangeNotifier?.removeListener(listener);
}
return remove;
}
}

/// Implementing ChangeNotifier
class ImplNotifyListenersChangeNotifier with ChangeNotifier {
/// Whether any listeners are currently registered.
@override
// ignore: unnecessary_overrides
bool get hasListeners => super.hasListeners;

/// Call all the registered listeners.
@override
// ignore: unnecessary_overrides
void notifyListeners() => super.notifyListeners();

// The 'unnecessary overrides' prevent the Dart Analysis warning:
// The member 'hasListeners' can only be used within instance members of
// subclasses of 'package: change_notifier.dart'.
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,15 @@

part of 'state_extended.dart';

/// setBuilder(WidgetBuilder? builder) rebuilds with every setState() call.
///
/// dartdoc:
/// {@category StateX class}
/// {@category State Object Controller}
mixin ListenableWidgetBuilderMixin {
// Widget builder allows for null
typedef MaybeBuildWidgetType = Widget? Function(BuildContext context);

/// Returns a widget from builder assuming the current object is a [Listenable]
/// const SizedBox.shrink() otherwise
Widget setBuilder(WidgetBuilder? builder) {
if (builder != null) {
_widgetBuilderUsed = true;
}
return _ListenableWidgetBuilder(
listenable: this,
builder: builder,
);
}

/// A flag. Noting if the function above is ever used.
bool get setBuilderUsed => _widgetBuilderUsed;
bool _widgetBuilderUsed = false;
}

/// Creates a widget that rebuilds when the given listenable calls.
/// Creates a widget that rebuilds when the given listenable calls
/// notifyListeners() function
class _ListenableWidgetBuilder extends StatefulWidget {
class ListenableWidgetBuilder extends StatefulWidget {
/// The arguments is not required.
const _ListenableWidgetBuilder({
const ListenableWidgetBuilder({
super.key,
this.listenable,
this.builder,
});
Expand All @@ -41,7 +21,7 @@ class _ListenableWidgetBuilder extends StatefulWidget {
final dynamic listenable;

/// Supply a Widget builder
final WidgetBuilder? builder;
final MaybeBuildWidgetType? builder;

/// Subclasses typically do not override this method.
@override
Expand All @@ -55,28 +35,31 @@ class _ListenableWidgetBuilder extends StatefulWidget {
}

//
class _ListenableState extends State<_ListenableWidgetBuilder> {
class _ListenableState extends State<ListenableWidgetBuilder> {
//
@override
void initState() {
super.initState();
// May be actually be a Listenable
// May actually be a Listenable
if (widget.builder != null && widget.listenable is Listenable) {
widgetBuilder = widget.builder!;
builder = widget.builder!;
listenable = widget.listenable;
listenable?.addListener(_callBuild);
} else {
// A widget must be provided. Even if it's nothing
widgetBuilder = (_) => const SizedBox.shrink();
builder = (_) => const SizedBox.shrink();
}
}

// Possibly not provided
Listenable? listenable;
// A Widget will be returned no matter what is provided.
late WidgetBuilder widgetBuilder;
late MaybeBuildWidgetType builder;
// Store the past widget displayed, and use it if builder returns null.
Widget? _widget;

@override
void didUpdateWidget(_ListenableWidgetBuilder oldWidget) {
void didUpdateWidget(ListenableWidgetBuilder oldWidget) {
super.didUpdateWidget(oldWidget);
// Possibly a O(2) expense if oldWidget == widget
if (oldWidget.listenable is Listenable) {
Expand All @@ -89,21 +72,29 @@ class _ListenableState extends State<_ListenableWidgetBuilder> {

@override
void dispose() {
_widget = null;
listenable?.removeListener(_callBuild);
listenable = null;
super.dispose();
}

void _callBuild() {
//
if (!mounted) {
return;
if (mounted) {
setState(() {
// Call the build() function again
});
}
setState(() {
// Call the build() function again
});
}

@override
Widget build(BuildContext context) => widgetBuilder.call(context);
Widget build(BuildContext context) {
final widget = builder.call(context);
// The builder is allowed to return null.
if (widget == null) {
_widget ??= const SizedBox.shrink();
} else {
_widget = widget;
}
return _widget!;
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loading

0 comments on commit be6fe1f

Please sign in to comment.