Skip to content

Commit 0335430

Browse files
committed
Move global configuration to dedicated chapter
- Create new global_configuration.md with all static Command properties - Remove Global Configuration section from command_properties.md - Update navigation to include Global Configuration after Command Results - Add Global Configuration link to See Also section - Update command_properties.md with improved error handling examples - Add error handling section to without_watch_it.md - Update CommandResult structure in command_results.md
1 parent 484e0cb commit 0335430

5 files changed

Lines changed: 321 additions & 106 deletions

File tree

docs/.vitepress/config.mts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export default defineConfig({
9999
{ text: 'Command Types', link: '/documentation/command_it/command_types' },
100100
{ text: 'Command Properties', link: '/documentation/command_it/command_properties' },
101101
{ text: 'Command Results', link: '/documentation/command_it/command_results' },
102+
{ text: 'Global Configuration', link: '/documentation/command_it/global_configuration' },
102103
{ text: 'Command Builders', link: '/documentation/command_it/command_builders' },
103104
{ text: 'Error Handling', link: '/documentation/command_it/error_handling' },
104105
{ text: 'Restrictions', link: '/documentation/command_it/restrictions' },
@@ -251,6 +252,7 @@ export default defineConfig({
251252
{ text: 'Command Types', link: '/documentation/command_it/command_types.md' },
252253
{ text: 'Command Properties', link: '/documentation/command_it/command_properties.md' },
253254
{ text: 'Command Results', link: '/documentation/command_it/command_results.md' },
255+
{ text: 'Global Configuration', link: '/documentation/command_it/global_configuration.md' },
254256
{ text: 'Command Builders', link: '/documentation/command_it/command_builders.md' },
255257
{ text: 'Error Handling', link: '/documentation/command_it/error_handling.md' },
256258
{ text: 'Restrictions', link: '/documentation/command_it/restrictions.md' },

docs/documentation/command_it/command_properties.md

Lines changed: 116 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Commands expose multiple `ValueListenable` properties for different aspects of e
44

55
## Overview
66

7+
### Instance Properties
8+
79
Every command provides these observable properties:
810

911
| Property | Type | Purpose |
@@ -18,6 +20,21 @@ Every command provides these observable properties:
1820
| [**name**](#name---debug-identifier) | `String?` | Debug name identifier |
1921
| [**clearErrors()**](#clearerrors---clear-error-state) | `void` | Clear error state manually |
2022

23+
### Global Configuration
24+
25+
Static properties that affect all commands in the app:
26+
27+
| Property | Type | Default | Purpose |
28+
|----------|------|---------|---------|
29+
| [**globalExceptionHandler**](#globalexceptionhandler) | `Function?` | `null` | Global error handler for all commands |
30+
| [**errorFilterDefault**](#errorfilterdefault) | `ErrorFilter` | `ErrorHandlerGlobalIfNoLocal()` | Default error filter |
31+
| [**assertionsAlwaysThrow**](#assertionsalwaysthrow) | `bool` | `true` | AssertionErrors bypass filters |
32+
| [**reportAllExceptions**](#reportallexceptions) | `bool` | `false` | Override filters, report all errors |
33+
| [**detailedStackTraces**](#detailedstacktraces) | `bool` | `true` | Enhanced stack traces |
34+
| [**loggingHandler**](#logginghandler) | `Function?` | `null` | Handler for all command executions |
35+
| [**reportErrorHandlerExceptionsToGlobalHandler**](#reporterrorhandlerexceptionstoglobalhandler) | `bool` | `true` | Report error handler exceptions |
36+
| [**useChainCapture**](#usechaincapture) | `bool` | `false` | Experimental detailed traces |
37+
2138
::: warning Sync Commands and isRunning
2239
**Accessing `.isRunning` on sync commands throws an assertion error.** Sync commands execute immediately without giving the UI time to react, so tracking execution state isn't meaningful.
2340

@@ -167,92 +184,71 @@ ValueListenableBuilder<bool>(
167184

168185
## errors - Error Notifications
169186

170-
Stream of errors that occur during execution:
171-
172-
<<< @/../code_samples/lib/command_it/error_handling_basic_example.dart#example
187+
Notifies when errors occur during execution:
173188

174189
**Behavior:**
175-
- Emits `null` at start of execution (clears previous error)
176-
- Emits `CommandError<TParam>` if function throws
190+
- Is set to `null` at start of execution (clears previous error without notification)
191+
- Notifies with `CommandError<TParam>` if function throws
177192
- `CommandError` contains:
178193
- `error`: The thrown exception
179194
- `paramData`: Parameter passed to command
180-
- `stackTrace`: Stack trace (if enabled)
181-
182-
**Filtering null values:**
183-
184-
```dart
185-
command.errors.where((e) => e != null).listen((error, _) {
186-
// Only called for actual errors, not null clears
187-
showErrorDialog(error!.error.toString());
188-
});
189-
```
195+
- `stackTrace`: Stack trace (enhanced if `Command.detailedStackTraces` is true)
190196

191197
**When to use:**
192198
- Show error dialogs
193199
- Display error messages
194200
- Log errors to analytics
195201
- Simple error handling without filters
196202

197-
## results - All Data Combined
198-
199-
Single property containing execution state, result, error, and parameter:
200-
201-
<<< @/../code_samples/lib/command_it/command_result_example.dart#example
202-
203-
**CommandResult properties:**
203+
**With watch_it:**
204204

205205
```dart
206-
class CommandResult<TParam, TResult> {
207-
final TParam? paramData; // Parameter passed to command
208-
final TResult? data; // Result value
209-
final bool isUndoValue; // True if this is from an undo operation
210-
final Object? error; // Error if thrown
211-
final bool isRunning; // Execution state
212-
final ErrorReaction? errorReaction; // How error was handled (if error occurred)
213-
final StackTrace? stackTrace; // Error stack trace (if error occurred)
214-
215-
// Convenience getters
216-
bool get hasData => data != null;
217-
bool get hasError => error != null && !isUndoValue; // Excludes undo errors
218-
bool get isSuccess => !isRunning && !hasError;
206+
class SaveWidget extends WatchingWidget {
207+
@override
208+
Widget build(BuildContext context) {
209+
final error = watchValue((DataManager m) => m.saveCommand.errors);
210+
211+
// Display error message if present
212+
return Column(
213+
children: [
214+
ElevatedButton(
215+
onPressed: () => di<DataManager>().saveCommand(data),
216+
child: Text('Save'),
217+
),
218+
if (error != null)
219+
ErrorBanner(
220+
message: error.error.toString(),
221+
onDismiss: () => di<DataManager>().saveCommand.clearErrors(),
222+
),
223+
],
224+
);
225+
}
219226
}
220227
```
221228

222-
**When to use:**
223-
- Single `ValueListenableBuilder` instead of multiple
224-
- Need parameter data for error messages
225-
- Want comprehensive state in one place
226-
- Using `CommandBuilder` widget
227-
228-
**Trade-off:** More updates (updates for running, success, error) vs convenience.
229+
**Without watch_it:** See [Using Commands without watch_it - Error Handling](/documentation/command_it/without_watch_it#error-handling)
229230

230-
## includeLastResultInCommandResults
231+
## results - All Data Combined
231232

232-
By default, `results.data` is `null` while loading or on error. Set `includeLastResultInCommandResults: true` to keep showing last success:
233+
Combines execution state, result data, errors, and parameters in a single observable:
233234

234235
```dart
235-
Command.createAsyncNoParam<List<Todo>>(
236-
() => api.fetchTodos(),
237-
initialValue: [],
238-
includeLastResultInCommandResults: true, // Keep showing old data
239-
);
236+
ValueListenableBuilder<CommandResult<TParam, TResult>>(
237+
valueListenable: command.results,
238+
builder: (context, result, _) {
239+
if (result.isRunning) return CircularProgressIndicator();
240+
if (result.hasError) return ErrorWidget(result.error);
241+
return DataWidget(result.data);
242+
},
243+
)
240244
```
241245

242-
**Behavior:**
243-
244-
| State | `includeLastResultInCommandResults: false` | `includeLastResultInCommandResults: true` |
245-
|-------|-------------------------------------------|------------------------------------------|
246-
| Initial | `data = []` | `data = []` |
247-
| First load success | `data = [todos]` | `data = [todos]` |
248-
| Second load (running) | `data = null` | `data = [todos]` (keeps old) |
249-
| Second load error | `data = null` | `data = [todos]` (keeps old) |
250-
| Second load success | `data = [new todos]` | `data = [new todos]` |
251-
252246
**When to use:**
253-
- Keep displaying old data while refreshing
254-
- Avoid empty screens during reload
255-
- Better UX for pull-to-refresh scenarios
247+
- Single `ValueListenableBuilder` instead of multiple nested builders
248+
- Need comprehensive state (running, data, error) in one place
249+
- Want parameter data for error messages or retry logic
250+
251+
**See [Command Results](/documentation/command_it/command_results) for complete CommandResult structure, examples, and the `includeLastResultInCommandResults` parameter.**
256252

257253
## errorsDynamic - Dynamic Error Type
258254

@@ -271,13 +267,13 @@ ValueListenable<CommandError<dynamic>?> get errorsDynamic => _errors;
271267
final saveCommand = Command.createAsync<Data, void>(...);
272268
final deleteCommand = Command.createAsync<String, void>(...);
273269
274-
// Both can share the same error handler
275-
void handleError(CommandError<dynamic>? error, _) {
276-
if (error != null) showErrorDialog(error.error.toString());
277-
}
278-
279-
saveCommand.errorsDynamic.listen(handleError);
280-
deleteCommand.errorsDynamic.listen(handleError);
270+
// Merge errors into single stream using listen_it
271+
[saveCommand.errorsDynamic, deleteCommand.errorsDynamic]
272+
.merge()
273+
.where((error) => error != null)
274+
.listen((error, _) {
275+
showErrorDialog(error!.error.toString());
276+
});
281277
```
282278

283279
## clearErrors() - Clear Error State
@@ -291,25 +287,66 @@ void clearErrors()
291287
**Behavior:**
292288
- Sets `errors.value` to `null`
293289
- Explicitly calls `notifyListeners()` to update UI
294-
- Useful when dismissing error dialogs/messages
295290

296291
**When to use:**
297-
- User dismisses an error message
298-
- Manually clearing error state before retry
299-
- Implementing custom error handling flows
292+
- You're watching errors in UI and want to hide error display without waiting for next execution
293+
- Implementing custom error recovery flows
294+
295+
```dart
296+
// Example: Dismissible error banner
297+
class MyWidget extends WatchingWidget {
298+
@override
299+
Widget build(BuildContext context) {
300+
final error = watchValue((Manager m) => m.command.errors);
301+
302+
return Column(
303+
children: [
304+
if (error != null)
305+
ErrorBanner(
306+
error: error.error.toString(),
307+
onDismiss: () => di<Manager>().command.clearErrors(),
308+
),
309+
// ... rest of UI
310+
],
311+
);
312+
}
313+
}
314+
```
315+
316+
::: tip Using listen/registerHandler - No Clear Needed
317+
If you use `.listen()` or `registerHandler()` to watch errors, they only get called when a new error appears (not when cleared to null). In this case, you typically don't need `clearErrors()` at all:
318+
319+
**With `.listen()`:**
320+
```dart
321+
command.errors.listen((error, _) {
322+
showSnackBar(error!.error.toString()); // Shows once per error, never null
323+
});
324+
```
325+
326+
**With `registerHandler()` (watch_it):**
327+
```dart
328+
registerHandler((Manager m) => m.command.errors, (context, error, cancel) {
329+
showSnackBar(error!.error.toString()); // Shows once per error, never null
330+
});
331+
```
332+
333+
Since listeners only fire on actual errors (never null), each error is shown once and you don't need to manually clear.
334+
335+
**Important:** If you DO call `clearErrors()` elsewhere in your code, handlers will receive `null` when the error is cleared. In that case, add a null check:
300336

301337
```dart
302-
// Error handling with manual dismissal
303338
command.errors.listen((error, _) {
304339
if (error != null) {
305-
showErrorDialog(
306-
error.error.toString(),
307-
onDismiss: () => command.clearErrors(),
308-
);
340+
showSnackBar(error.error.toString());
309341
}
310342
});
311343
```
312344

345+
**Use `clearErrors()` when:**
346+
- Watching errors with `watchValue` - rebuilds on every change, needs manual clear to hide UI
347+
- Conditionally showing error widgets based on error state
348+
:::
349+
313350
::: tip Clearing Errors Without Notification
314351
You can also set `command.errors.value = null` directly to clear the error WITHOUT triggering listeners. This is useful if you want to silently reset the error state.
315352

@@ -318,8 +355,6 @@ You can also set `command.errors.value = null` directly to clear the error WITHO
318355
Use `clearErrors()` when you want UI updates (e.g., dismissing error messages). Use direct assignment when you don't.
319356
:::
320357

321-
**Note:** Preferred error handling is via `registerHandler` or `listen` in `initState` of a `StatefulWidget`, but `clearErrors()` is especially useful with watch_it.
322-
323358
## name - Debug Identifier
324359

325360
Returns the debug name set via `debugName` parameter:
@@ -372,29 +407,10 @@ command.errors.listen((error, _) => showError(error))
372407
ValueListenableBuilder(valueListenable: command.results, ...)
373408
```
374409

375-
## Watching Commands with watch_it
376-
377-
If using watch_it, you can watch command properties without builders:
378-
379-
```dart
380-
class TodoWidget extends WatchingWidget {
381-
@override
382-
Widget build(BuildContext context) {
383-
final todos = watchValue((TodoManager m) => m.loadCommand);
384-
final isLoading = watchValue((TodoManager m) => m.loadCommand.isRunning);
385-
final canRun = watchValue((TodoManager m) => m.loadCommand.canRun);
386-
387-
if (isLoading) return CircularProgressIndicator();
388-
return TodoList(todos: todos);
389-
}
390-
}
391-
```
392-
393-
See [Integration with watch_it](/documentation/command_it/watch_it_integration) for details.
394-
395410
## See Also
396411

397412
- [Command Basics](/documentation/command_it/command_basics) — Creating and running commands
398413
- [Command Results](/documentation/command_it/command_results) — Deep dive into CommandResult
414+
- [Global Configuration](/documentation/command_it/global_configuration) — Static properties reference
399415
- [Error Handling](/documentation/command_it/error_handling) — Handling errors
400416
- [Command Restrictions](/documentation/command_it/restrictions) — Conditional execution

docs/documentation/command_it/command_results.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@ Deep dive into `CommandResult` - the comprehensive state object that combines ex
88

99
```dart
1010
class CommandResult<TParam, TResult> {
11-
final TParam? paramData; // Parameter passed to command
12-
final TResult? data; // Result value (null while running or on error)
13-
final Object? error; // Error if thrown
14-
final bool isRunning; // Current execution state
11+
final TParam? paramData; // Parameter passed to command
12+
final TResult? data; // Result value
13+
final bool isUndoValue; // True if this is from an undo operation
14+
final Object? error; // Error if thrown
15+
final bool isRunning; // Execution state
16+
final ErrorReaction? errorReaction; // How error was handled (if error occurred)
17+
final StackTrace? stackTrace; // Error stack trace (if error occurred)
1518
1619
// Convenience getters
1720
bool get hasData => data != null;
18-
bool get hasError => error != null;
19-
bool get isSuccess => !hasError && !isRunning;
21+
bool get hasError => error != null && !isUndoValue; // Excludes undo errors
22+
bool get isSuccess => !isRunning && !hasError;
2023
}
2124
```
2225

0 commit comments

Comments
 (0)