Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add documentation #8

Merged
merged 82 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
eb8ed1f
Add documentation
manuel-plavsic Jan 26, 2025
e565dfa
Rename file
manuel-plavsic Jan 26, 2025
ae0b741
Rename alternatives to miscellaneous
manuel-plavsic Jan 26, 2025
937dbf2
Improve docs
manuel-plavsic Jan 26, 2025
22780df
remove file prefixes
manuel-plavsic Jan 26, 2025
38222a0
Make small adjustments
manuel-plavsic Jan 26, 2025
9825b21
Make small correction
manuel-plavsic Jan 26, 2025
1009940
docs: updates
nank1ro Jan 27, 2025
2d13117
docs: add basic example
nank1ro Jan 27, 2025
ea7554e
docs: update sentence
nank1ro Jan 27, 2025
780c9ed
Add Installing page
manuel-plavsic Jan 27, 2025
af99e54
Reframe sentence
manuel-plavsic Jan 27, 2025
dd57382
Remove service_injection example
manuel-plavsic Jan 27, 2025
bb1a4aa
Prepare all examples
manuel-plavsic Jan 27, 2025
d0683b2
Fix typo
manuel-plavsic Jan 27, 2025
b4cfd62
Merge branch 'main' into feat/documentation
nank1ro Jan 27, 2025
2be31b9
Merge branch 'main' into feat/documentation
nank1ro Jan 27, 2025
917a6c7
docs: auto route
nank1ro Jan 27, 2025
4d19ab9
docs: update
nank1ro Jan 27, 2025
3deeb9e
docs: add file structure to bloc
nank1ro Jan 27, 2025
497a778
docs: use link card for source code
nank1ro Jan 27, 2025
cf00e9a
docs: update installing
nank1ro Jan 27, 2025
f3e2eda
docs: add empty line
nank1ro Jan 27, 2025
dd03b50
docs: solidart
nank1ro Jan 27, 2025
d7afcf3
Mention that global state solutions usually use extra objects to mana…
manuel-plavsic Jan 27, 2025
18fe128
Make small corrections
manuel-plavsic Jan 27, 2025
0aca5b0
Make small improvements
manuel-plavsic Jan 27, 2025
b588122
Refine bloc example
manuel-plavsic Jan 28, 2025
056fd32
docs: add example deps
nank1ro Jan 28, 2025
56d8d9a
docs: add dependency
nank1ro Jan 28, 2025
1afbffe
docs: authors
nank1ro Jan 28, 2025
15486af
docs: update bio
nank1ro Jan 28, 2025
a5205d1
docs: author card add social links
nank1ro Jan 28, 2025
7caac1d
docs: update
nank1ro Jan 28, 2025
790aa24
docs: graphical representation of provider scope
nank1ro Jan 28, 2025
aa0a563
docs: update provider scope graph
nank1ro Jan 28, 2025
195fbe9
docs: add provider scope exception
nank1ro Jan 28, 2025
95286e5
docs: theme image
nank1ro Jan 28, 2025
8057b0d
Improve authors section
manuel-plavsic Jan 28, 2025
50f5df6
Improve my own introduction
manuel-plavsic Jan 28, 2025
df7f25b
Improve authors
manuel-plavsic Jan 28, 2025
ab170c4
Small correction
manuel-plavsic Jan 28, 2025
1fe11be
Improve main page title and description
manuel-plavsic Jan 28, 2025
80492d8
Move author section to the end but also add link in index
manuel-plavsic Jan 28, 2025
78ab6bd
docs: add provider scope portal images
nank1ro Jan 28, 2025
95b43ca
docs: update authors
nank1ro Jan 28, 2025
4afd23e
docs: fix typo
nank1ro Jan 28, 2025
a0f049f
docs: add how providers are found graph
nank1ro Jan 28, 2025
958979e
docs: add note about big o notation
nank1ro Jan 28, 2025
af5c2e3
Make changes
manuel-plavsic Jan 28, 2025
f2c6692
Merge branch 'main' into feat/documentation
nank1ro Jan 28, 2025
7369695
Rename to Provider Retrieval Process
manuel-plavsic Jan 28, 2025
dfd6f7b
Add warnings
manuel-plavsic Jan 28, 2025
4b3daf8
Add comma
manuel-plavsic Jan 28, 2025
7bcf2e6
Fix typo
manuel-plavsic Jan 28, 2025
c246e41
Use notes and tips in testing and make other small improvements
manuel-plavsic Jan 28, 2025
9e64e67
Add example heading inside modals.mdx
manuel-plavsic Jan 28, 2025
5cacd72
Add two example headings to reactivity.md
manuel-plavsic Jan 28, 2025
e86f493
Reframe sentence
manuel-plavsic Jan 28, 2025
cca9c91
Rename heading in index
manuel-plavsic Jan 28, 2025
d859d6f
Clarify comment in reactivity.md
manuel-plavsic Jan 28, 2025
90c3c29
Do not capitalize "providers"
manuel-plavsic Jan 28, 2025
3f7108a
Remove unnecessary parts and make some fixes.
manuel-plavsic Jan 28, 2025
eb703d4
Make improvements to scoped-di.mdx
manuel-plavsic Jan 28, 2025
ec7a97d
Fix typos and reframe some sentences.
manuel-plavsic Jan 29, 2025
e79d1d8
Make small adjustment
manuel-plavsic Jan 29, 2025
0725865
Fix headings
manuel-plavsic Jan 29, 2025
a2f3ef9
Fix other headings
manuel-plavsic Jan 29, 2025
78996c3
Reframe text and change its order
manuel-plavsic Jan 29, 2025
17bc916
Simplify sentence in authors.mdx
manuel-plavsic Jan 29, 2025
986a923
docs: fix import
nank1ro Jan 29, 2025
17c6959
docs: fix typos
nank1ro Jan 29, 2025
aafce59
docs: fix variablee not useed
nank1ro Jan 29, 2025
968173c
Merge branch 'main' into feat/documentation
nank1ro Jan 29, 2025
416beaf
docs: preferences example
nank1ro Jan 29, 2025
ae06873
docs: preferences
nank1ro Jan 29, 2025
ddcff31
docs: immutability
nank1ro Jan 29, 2025
70c25cf
docs: remove prefixes from code title
nank1ro Jan 29, 2025
2d82a9c
docs: update var name in immutability
nank1ro Jan 29, 2025
4cc17de
Make improvements
manuel-plavsic Jan 29, 2025
d604538
fix typo
manuel-plavsic Jan 29, 2025
14894aa
Change from examples to single example in preferences.mdx
manuel-plavsic Jan 29, 2025
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
21 changes: 19 additions & 2 deletions docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,25 @@ export default defineConfig({
},
sidebar: [
{
label: 'Guides',
autogenerate: { directory: 'guides' },
label: 'Overview',
link: '',
},
{
label: 'Core',
items: [
'core/providers',
'core/scoped-di',
'core/modals',
'core/testing',
'core/configuration',
],
},
{
label: 'Miscellaneous',
items: [
'miscellaneous/reactivity',
'miscellaneous/comparison-with-alternatives',
],
},
],
}),
Expand Down
Binary file added docs/public/disco.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/src/assets/houston.webp
Binary file not shown.
19 changes: 19 additions & 0 deletions docs/src/content/docs/core/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
title: Configuration
description: How to set up the application-wide configuration for Disco.
---

Disco aims to be minimally opinionated. To customize the default configuration, set the desired preferences using `DiscoConfig` before calling `runApp`. For example:

```dart
DiscoConfig.lazy = false;
runApp(
// ...
);
```

### All options

| Option | Default | Description |
| -------------- | ------- | ----------- |
| `lazy` | true | The values of the providers provided in a `ProviderScope` are created lazily. |
52 changes: 52 additions & 0 deletions docs/src/content/docs/core/modals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
title: Modals
description: How to access providers inside modals.
---

A modal spawns a new widget tree, making injecting providers not possible out of the box.

This library offers `ProviderScopePortal`, which gives access to all the providers that were created in the main widget tree.

Note that you have to pass it the context of the main tree for it to work. Also, a `BuildContext` that is a descendant of `ProviderScopePortal` needs to be used. This is why in the following example we created a `Builder` and used its argument `innerContext` to inject the provider.

```dart
final numberProvider = Provider((context) => 1);

Future<void> showNumberDialog({required BuildContext context}) {
return showDialog(
context: context,
builder: (dialogContext) {
return ProviderScopePortal(
mainContext: context,
child: Builder(
builder: (innerContext) {
final number =
numberProvider.of(innerContext);
return Text('$number');
},
),
);
},
);
}

runApp(
MaterialApp(
home: Scaffold(
body: ProviderScope(
providers: [numberProvider],
child: Builder(
builder: (context) {
return ElevatedButton(
onPressed: () {
showNumberDialog(context: context);
},
child: const Text('show dialog'),
);
},
),
),
),
),
);
```
83 changes: 83 additions & 0 deletions docs/src/content/docs/core/providers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
title: Providers
description: How to create Disco providers.
---

A provider is a tool that helps manage and inject dependencies in an application, making it easier to share data or services across different parts of the app.

**NB:** When we use the term "provider", it can refer to either the `Provider` class of this library or the value it contains, depending on the context.

## Providers

Declare a new provider either as a global `final` variable or a `final` static field.

**NB:** while providers can be declared globally, they **do not function globally**. They are just used as **identifiers** when registered in a scope.

Example with a global `final` variable.

```dart
/// global scope
final numberProvider = Provider((context) => 5);
```

If there is only a provider per class, you can also create a `final` static field. This comes down to personal preference.

```dart
class MyDatabase {
static final provider = Provider((context) => MyDatabase());
}
```

### Injection of other providers with context

Providers can leverage the context to inject other providers. The context will be relative to the scope in which they are provided.

```dart
final doubleNumberProvider = Provider((context) {
final number = numberProvider.of(context);
return number * 2;
});
```

## Providers with argument

Providers need to be provided before they can be injected in the widget tree. Sometimes, they need an initial argument so that they can be instantiated correctly. This is possible with `Provider.withArgument`.

```dart
final numberPlusArgProvider = Provider.withArgument((context, int arg) {
return 5 + arg;
});
```

An example where this might make more sense would be an application with multi-account support, where the database is loaded per user, and the filepath of the database contains the user ID:

```dart
class MyDatabase {
static final provider = Provider.withArgument((context, String userId) => MyDatabase.fromId(id));
}
```

This `MyDatabase.provider` has to be provided in a subtree (of the widget tree) belonging to the currently logged user.

### Injection of other providers with context

Providers can both take an argument and rely on context.

```dart
final doubleNumberPlusArgProvider = Provider.withArgument((context, int arg) {
final number = numberProvider.of(context);
return number * 2 + arg;
});
```

## Dispose and lazy parameters

When defining a provider, we need to pass the positional `create` argument, which is a function used to generate the value contained by the provider.

There are also two optional named parameters that can be specified.

| Parameter | Default | Description |
| -------------- | ------- | ----------- |
| `dispose` | null | The function to call when the scope containing the provider gets disposed. It is used to dispose correctly the value held by the provider. |
| `lazy` | `DiscoConfig.lazy`, which defaults to true | The values of the providers provided in a scope are created lazily.|

106 changes: 106 additions & 0 deletions docs/src/content/docs/core/scoped-di.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
---
title: Scoped DI
description: How to use Disco providers and understanding scoping.
---

Providers have to be provided before they can be injected.

Let us consider two providers of the previous page for this section.

```dart
final numberProvider = Provider((context) => 5);

final doubleNumberPlusArgProvider = Provider.withArgument((context, int arg) {
final number = numberProvider.of(context);
return number * 2 + arg;
});
```

### How to scope

Scoping means that the provider must be specified within a `ProviderScope` before it can be injected.

In case the provider does not take an argument, we scope it the following way:

```dart
ProviderScope(
providers: [numberProvider]
child: // ...
)
```

In case the provider takes an argument, we need to specify it when providing it.

```dart
ProviderScope(
providers: [doubleNumberPlusArgProvider(10)]
child: // ...
)
```

### How to inject

Injecting is the act of retrieving a dependency. It is done with the methods `of(context)` and `maybeOf(context)`, the latter one being safer because it returns null instead of throwing if the provider is not found in any scopes. If you are unsure about which one to use, for simplicity you should probably stick to `of(context)` (and maybe set up an error monitoring solution to automatically detect invalid injections).

We inject the two providers above by using the `numberProvider.of(context)` and `doubleNumberPlusArgProvider.of(context)`.

### Full example

Try and guess what the displayed text will be before reading the solution.

```dart
runApp(
MaterialApp(
home: Scaffold(
body: ProviderScope(
providers: [numberProvider],
child: ProviderScope(
providers: [doubleNumberPlusArgProvider(10)],
child: Builder(
builder: (context) {
final number = numberProvider.of(context);
final doubleNumberPlusArg = doubleNumberPlusArgProvider.of(context);
return Text('$number $doubleNumberPlusArg');
},
),
),
),
),
),
);
```

The solution is "5 20".

## Scoping correctly

Some providers might have a dependency on other providers or as an argument. It is important that the following considerations are made.

### Context

Note that the scope containing `doubleNumberPlusArgProvider` needs to be a descendant of the one containing `numberProvider`. This is because `doubleNumberPlusArgProvider` uses the context to find the value of `numberProvider`. The following will thus **not** work:

```dart
// bad example
ProviderScope(
providers: [
numberProvider,
doubleNumberPlusArgProvider(10),
],
child: // ...
)
```

Placing the `ProviderScope` containing `doubleNumberPlusArgProvider` above the one containing `numberProvider` would also not work. It needs to be like in the full example above.

### Argument

Let's recall the example from the previous page.

```dart
class MyDatabase {
static final provider = Provider((context, String userId) => MyDatabase.fromId(id));
}
```

The `MyDatabase.provider` should be provided in a subtree (of the widget tree) belonging to the currently logged user. If you provide it too high up in the widget tree (above the currently logged user logic) it will not be possible to change database, because the `ProviderScope` where `MyDatabase.provider` is provided is never disposed. In practice, this particular error is unlikely due to the `userId` being first available in the currently logged user logic. However, this kind of scenario needs to be considered.
70 changes: 70 additions & 0 deletions docs/src/content/docs/core/testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
title: Testing
description: How to use overrides for testing.
---

Testing is done with overrides. You will need to place a `ProviderScopeOverride` (preferably as the root widget) and then specify the `overrides`, which is a list containing the providers followed by `.overrideWithValue(T value)`.

Note that you can **only** use one `ProviderScopeOverride` per test.

The text displayed in the following example will be "100".

```dart
testWidgets(
'''ProviderScopeOverride should override providers''',
(tester) async {
final numberProvider = Provider<int>((context) => 0);
await tester.pumpWidget(
ProviderScopeOverride(
overrides: [
numberProvider.overrideWithValue(100),
],
child: MaterialApp(
home: ProviderScope(
providers: [
numberProvider,
],
child: Builder(
builder: (context) {
final number = numberProvider.of(context);
return Text(number.toString());
},
),
),
),
),
);
expect(find.text('100'), findsOneWidget);
});
```

Testing is possible also with providers that take an argument, and it is done same exact way. The text displayed in the following example will be "16".

```dart
testWidgets(
'''ProviderScopeOverride should override argument providers''',
(tester) async {
final numberProvider = Provider.withArgument((context, int arg) => arg * 2);
await tester.pumpWidget(
ProviderScopeOverride(
overrides: [
numberProvider.overrideWithValue(8),
],
child: MaterialApp(
home: ProviderScope(
providers: [
numberProvider(1),
],
child: Builder(
builder: (context) {
final number = numberProvider.of(context);
return Text(number.toString());
},
),
),
),
),
);
expect(find.text('16'), findsOneWidget);
});
```
11 changes: 0 additions & 11 deletions docs/src/content/docs/guides/example.md

This file was deleted.

Loading
Loading