Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/docs/flutter_modular/dependency-injection.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 3
sidebar_position: 4
---

# Dependency Injection
Expand Down
2 changes: 1 addition & 1 deletion doc/docs/flutter_modular/module.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 4
sidebar_position: 5
---

# Module
Expand Down
2 changes: 1 addition & 1 deletion doc/docs/flutter_modular/test.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 6
sidebar_position: 7
---

# Tests
Expand Down
2 changes: 1 addition & 1 deletion doc/docs/flutter_modular/triple-integration.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 7
sidebar_position: 8
---

# Triple Pattern integration
Expand Down
72 changes: 72 additions & 0 deletions doc/docs/flutter_modular/watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
sidebar_position: 3
---

# Watch

Modular also has an InheritedWidget instance that can use the `dependsOn` API of [BuildContext].
This way, the developer can recover binds and, if it is a supported reactivity, will watch the modifications
changing the state of the widget in question.

The `watch()` method was added to [BuildContext] through extensions, making access easy.
```dart
class Body extends StatelessWidget {
Widget build(BuildContext context){
final notifier = context.watch<ValueNotifier>();
return Text('${notifier.value}')
}
}
```

Supported reactivities are:
- [Listenable](https://api.flutter.dev/flutter/foundation/Listenable-class.html)

Used in `ChangeNotifier/ValueNotifier` classes and in RxNotifier.

- [Stream](https://api.dart.dev/stable/2.15.0/dart-async/Stream-class.html)

Used in StreamController or BLoC/Cubit

- [Store](https://triple.flutterando.com.br/docs/getting-started/using-flutter-triple)

Used in Triple Pattern in StreamStore and NotifierStore classes.

:::tip TIP

In addition to the **context.watch()** method, the read-only **context.read()** method has been added.
It's the same as using **Modular.get()**, but this addition helps projects that are being migrated
**Provider**.

:::

## With selectors

Sometimes binds are not a supported reactivity, but one of their properties can be.
As in the case of BLoC, where the Stream is available through a `bloc.stream` property;

We can add a selection through an anonymous function indicating which property is a supported reactivity to be watched:

```dart
class Body extends StatelessWidget {
Widget build(BuildContext context){
final bloc = context.watch<CounterBloc>((bloc) => bloc.stream);
return Text('${bloc.state}')
}
}
```

Note that the use of the selector does not change on bind return.

We can also use selectors for Triple objects, which have their own selectors for each of their segments:
See the Triple documentation for more details [by clicking here](https://triple.flutterando.com.br/docs/getting-started/using-flutter-triple#selectors):

```dart
class OnlyErrorWidget extends StatelessWidget {
Widget build(BuildContext context){
// changes with store.setError();
final store = context.watch<MyTripleStore>((store) => store.selectError);
return Text('${store.error}')
}
}
```

2 changes: 1 addition & 1 deletion doc/docs/flutter_modular/widgets.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 5
sidebar_position: 6
---

# Widgets
Expand Down
2 changes: 2 additions & 0 deletions doc/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions flutter_modular/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,37 @@
## [4.3.0] - 2021-12-10
* Added BuildContext extension [context.read()] and [context.watch()];
* The [context.watch()] listen changes of [Listanable], [Stream] and [Store] by Triple;
```dart
class Body extends StatelessWidget {
Widget build(BuildContext context){
final notifier = context.watch<ValueNotifier>();
return Text('${notifier.value}')
}
}
```
* Use `select` in `.watch()` to select the reactive property:
```dart
class Body extends StatelessWidget {
Widget build(BuildContext context){
final bloc = context.watch<CounterBloc>((bloc) => bloc.stream);
return Text('${bloc.state}')
}
}
```

Also, use `Store Selectors` in conjunction with `.watch`:
```dart
class OnlyErrorWidget extends StatelessWidget {
Widget build(BuildContext context){
// changes with store.setError();
final store = context.watch<MyTripleStore>((store) => store.selectError);
return Text('${store.error}')
}
}
```

See more details [here](https://modular.flutterando.com.br/docs/flutter_modular/watch)

## [4.2.0] - 2021-10-28
* Added cleanInjector() and cleanModular() for restart Modular. [#601](https://github.com/Flutterando/modular/pull/601)
* Updated modular_core.
Expand Down
130 changes: 125 additions & 5 deletions flutter_modular/lib/src/presenter/widgets/modular_app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ class ModularApp extends StatefulWidget {
/// Prohibits taking any bind of parent modules, forcing the imports of the same in the current module to be accessed. This is the same behavior as the system. Default is false;
bool notAllowedParentBinds = false,
}) : super(key: key) {
(Modular as ModularBase).flags.experimentalNotAllowedParentBinds =
notAllowedParentBinds;
(Modular as ModularBase).flags.experimentalNotAllowedParentBinds = notAllowedParentBinds;
(Modular as ModularBase).flags.isDebug = debugMode;
}

Expand All @@ -51,8 +50,7 @@ class ModularAppState extends State<ModularApp> {
@override
void dispose() {
Modular.destroy();
Modular.debugPrintModular(
'-- ${widget.module.runtimeType.toString()} DISPOSED');
Modular.debugPrintModular('-- ${widget.module.runtimeType.toString()} DISPOSED');
cleanGlobals();
super.dispose();
}
Expand All @@ -65,6 +63,128 @@ class ModularAppState extends State<ModularApp> {

@override
Widget build(BuildContext context) {
return widget.child;
return _ModularInherited(child: widget.child);
}
}

typedef SelectCallback<T> = Function(T bind);

class _Register<T> {
final T value;
Type get type => T;
final SelectCallback<T>? _select;

_Register(this.value, this._select);

dynamic getSelected() => _select != null ? _select!(value) : value;

@override
bool operator ==(Object object) => identical(this, object) || object is _Register && runtimeType == object.runtimeType && type == object.type;

@override
int get hashCode => value.hashCode ^ type.hashCode;
}

class _ModularInherited extends InheritedWidget {
const _ModularInherited({Key? key, required Widget child}) : super(key: key, child: child);

static T of<T extends Object>(BuildContext context, {bool listen = true, SelectCallback<T>? select}) {
final bind = Modular.get<T>();
if (listen) {
final registre = _Register<T>(bind, select);
final inherited = context.dependOnInheritedWidgetOfExactType<_ModularInherited>(aspect: registre)!;
inherited.updateShouldNotify(inherited);
}

return bind;
}

@override
bool updateShouldNotify(covariant InheritedWidget oldWidget) {
return false;
}

@override
InheritedElement createElement() => _InheritedModularElement(this);
}

class _InheritedModularElement extends InheritedElement {
_InheritedModularElement(InheritedWidget widget) : super(widget);

bool _dirty = false;

Type? current;

@override
void updateDependencies(Element dependent, covariant _Register aspect) {
var registers = getDependencies(dependent) as Set<_Register>?;

registers ??= {};

if (registers.contains(aspect)) {
return;
}

final value = aspect.getSelected();

if (value is Listenable) {
value.addListener(() => _handleUpdate(aspect.type));
} else if (value is Stream) {
value.listen((event) => _handleUpdate(aspect.type));
} else if (value is Store) {
value.observer(
onState: (state) => _handleUpdate(aspect.type),
onError: (error) => _handleUpdate(aspect.type),
onLoading: (isLoading) => _handleUpdate(aspect.type),
);
}
registers.add(aspect);
setDependencies(dependent, registers);
}

@override
Widget build() {
if (_dirty) notifyClients(widget);
return super.build();
}

void _handleUpdate(Type type) {
current = type;
_dirty = true;
markNeedsBuild();
}

@override
void notifyClients(InheritedWidget oldWidget) {
super.notifyClients(oldWidget);
_dirty = false;
current = null;
}

@override
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
var registers = getDependencies(dependent) as Set<_Register>?;
registers ??= {};

for (var register in registers) {
if (register.type == current) {
dependent.didChangeDependencies();
}
}
}
}

extension ModularWatchExtension on BuildContext {
/// Request an instance by [Type] and
/// watch your changes
///
/// SUPPORTED CLASS ([Listenable], [Stream] and [Store] by Triple).
T watch<T extends Object>([SelectCallback<T>? select]) {
return _ModularInherited.of<T>(this, select: select);
}

/// Request an instance by [Type]
T read<T extends Object>() {
return _ModularInherited.of<T>(this, listen: false);
}
}
2 changes: 1 addition & 1 deletion flutter_modular/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: flutter_modular
description: Smart project structure with dependency injection and route management
version: 4.2.0
version: 4.3.0
homepage: https://github.com/Flutterando/modular

environment:
Expand Down
Loading