Skip to content

Commit e59c781

Browse files
committed
Extract all inline code from observing_commands.md (8/12)
Created command_observing_patterns.dart with 8 regions: - watch_value_pattern - dont_await_execute_good/bad - watch_execution_state_good - handle_errors_good_watch/handler - initial_load_pattern - form_submission_pattern - pull_to_refresh_pattern - retry_on_error_pattern All 8 inline code blocks from observing_commands.md extracted. Examples compile successfully (9 info warnings only). Progress: 8 files done, 4 remaining.
1 parent 1761289 commit e59c781

File tree

2 files changed

+232
-108
lines changed

2 files changed

+232
-108
lines changed
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
// ignore_for_file: unused_local_variable, unreachable_from_main, undefined_class, unused_element, dead_code, invalid_use_of_visible_for_testing_member
2+
import 'package:flutter/material.dart';
3+
import 'package:watch_it/watch_it.dart';
4+
import '_shared/stubs.dart';
5+
6+
final di = GetIt.instance;
7+
8+
// #region watch_value_pattern
9+
// Get the command
10+
void watchValuePattern(BuildContext context) {
11+
final manager = di<WeatherManager>();
12+
13+
// Watch its value
14+
final weather = watch(manager.fetchWeatherCommand).value;
15+
final isLoading = watch(manager.fetchWeatherCommand.isExecuting).value;
16+
}
17+
// #endregion watch_value_pattern
18+
19+
// #region dont_await_execute_good
20+
// ✓ GOOD - Non-blocking, UI stays responsive
21+
class DontAwaitExecuteGood extends WatchingWidget {
22+
@override
23+
Widget build(BuildContext context) {
24+
return ElevatedButton(
25+
onPressed: () => di<TodoManager>().createTodoCommand.execute(
26+
CreateTodoParams(title: 'New todo', description: 'Description'),
27+
),
28+
child: Text('Submit'),
29+
);
30+
}
31+
}
32+
// #endregion dont_await_execute_good
33+
34+
// #region dont_await_execute_bad
35+
// ❌ BAD - Blocks UI thread
36+
class DontAwaitExecuteBad extends WatchingWidget {
37+
@override
38+
Widget build(BuildContext context) {
39+
return ElevatedButton(
40+
onPressed: () async {
41+
await di<TodoManager>().createTodoCommand.executeWithFuture(
42+
CreateTodoParams(title: 'New todo', description: 'Description'),
43+
);
44+
},
45+
child: Text('Submit'),
46+
);
47+
}
48+
}
49+
// #endregion dont_await_execute_bad
50+
51+
// Helper command for examples
52+
class Command {
53+
final isExecuting = ValueNotifier<bool>(false);
54+
final errors = ValueNotifier<String?>(null);
55+
final value = ValueNotifier<String?>(null);
56+
57+
void execute() {}
58+
Future<void> executeWithFuture() async {}
59+
}
60+
61+
// #region watch_execution_state_good
62+
// ✓ GOOD - Watch isExecuting
63+
class WatchExecutionStateGood extends WatchingWidget {
64+
@override
65+
Widget build(BuildContext context) {
66+
final command = createOnce(() => Command());
67+
final isLoading = watch(command.isExecuting).value;
68+
69+
if (isLoading) {
70+
return CircularProgressIndicator();
71+
}
72+
73+
return Container();
74+
}
75+
}
76+
// #endregion watch_execution_state_good
77+
78+
// #region handle_errors_good_watch
79+
// ✓ GOOD - Watch errors and display them
80+
class HandleErrorsGoodWatch extends WatchingWidget {
81+
@override
82+
Widget build(BuildContext context) {
83+
final command = createOnce(() => Command());
84+
final error = watch(command.errors).value;
85+
86+
if (error != null) {
87+
return Text('Error: $error');
88+
}
89+
90+
return Container();
91+
}
92+
}
93+
// #endregion handle_errors_good_watch
94+
95+
// #region handle_errors_good_handler
96+
// ✓ ALSO GOOD - Use handler for error dialog
97+
class HandleErrorsGoodHandler extends WatchingWidget {
98+
@override
99+
Widget build(BuildContext context) {
100+
registerHandler(
101+
select: (TodoManager m) => m.createTodoCommand.errors,
102+
handler: (context, error, _) {
103+
if (error != null) {
104+
showDialog(
105+
context: context,
106+
builder: (_) => AlertDialog(
107+
title: Text('Error'),
108+
content: Text('$error'),
109+
),
110+
);
111+
}
112+
},
113+
);
114+
return Container();
115+
}
116+
}
117+
// #endregion handle_errors_good_handler
118+
119+
// #region initial_load_pattern
120+
// Show spinner only when no data yet
121+
class InitialLoadPattern extends WatchingWidget {
122+
@override
123+
Widget build(BuildContext context) {
124+
final command = di<TodoManager>().fetchTodosCommand;
125+
final isLoading = watch(command.isExecuting).value;
126+
final data = watch(command).value;
127+
128+
// Show spinner only when no data yet
129+
if (isLoading && data.isEmpty) {
130+
return CircularProgressIndicator();
131+
}
132+
133+
// Show data even while refreshing
134+
return ListView(
135+
children: [
136+
if (isLoading) LinearProgressIndicator(), // Subtle indicator
137+
...data.map((item) => ListTile(title: Text(item.title))),
138+
],
139+
);
140+
}
141+
}
142+
// #endregion initial_load_pattern
143+
144+
// Helper class for form example
145+
class FormData {
146+
final String title;
147+
FormData(this.title);
148+
}
149+
150+
class Manager {
151+
final submitCommand = Command();
152+
}
153+
154+
// #region form_submission_pattern
155+
// Form submission pattern
156+
class FormSubmissionPattern extends WatchingWidget {
157+
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
158+
final formData = FormData('Example');
159+
160+
@override
161+
Widget build(BuildContext context) {
162+
final manager = di<Manager>();
163+
final isSubmitting = watch(manager.submitCommand.isExecuting).value;
164+
final canSubmit = formKey.currentState?.validate() ?? false;
165+
166+
return ElevatedButton(
167+
onPressed: canSubmit && !isSubmitting
168+
? () => manager.submitCommand.execute()
169+
: null,
170+
child: isSubmitting ? CircularProgressIndicator() : Text('Submit'),
171+
);
172+
}
173+
}
174+
// #endregion form_submission_pattern
175+
176+
// #region pull_to_refresh_pattern
177+
// Pull to refresh pattern
178+
class PullToRefreshPattern extends WatchingWidget {
179+
@override
180+
Widget build(BuildContext context) {
181+
final manager = di<TodoManager>();
182+
183+
return RefreshIndicator(
184+
onRefresh: () async {
185+
manager.fetchTodosCommand.execute();
186+
await manager.fetchTodosCommand.executeWithFuture();
187+
},
188+
child: ListView(children: []),
189+
);
190+
}
191+
}
192+
// #endregion pull_to_refresh_pattern
193+
194+
// #region retry_on_error_pattern
195+
// Retry on error pattern
196+
class RetryOnErrorPattern extends WatchingWidget {
197+
@override
198+
Widget build(BuildContext context) {
199+
final command = createOnce(() => Command());
200+
final error = watch(command.errors).value;
201+
202+
if (error != null) {
203+
return Column(
204+
children: [
205+
Text('Error: $error'),
206+
ElevatedButton(
207+
onPressed: () => command.execute(),
208+
child: Text('Retry'),
209+
),
210+
],
211+
);
212+
}
213+
214+
return Container();
215+
}
216+
}
217+
// #endregion retry_on_error_pattern
218+
219+
void main() {}

docs/documentation/watch_it/observing_commands.md

Lines changed: 13 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,8 @@ Watch the command's `value` property to display results:
3232
<<< @/../code_samples/lib/watch_it/watch_command_value_example.dart#example
3333

3434
**Pattern:**
35-
```dart
36-
// Get the command
37-
final manager = di<WeatherManager>();
3835

39-
// Watch its value
40-
final weather = watch(manager.fetchWeatherCommand).value;
41-
final isLoading = watch(manager.fetchWeatherCommand.isExecuting).value;
42-
```
36+
<<< @/../code_samples/lib/watch_it/command_observing_patterns.dart#watch_value_pattern
4337

4438
## Watching Command Errors
4539

@@ -132,130 +126,41 @@ Use handlers to chain commands together:
132126

133127
### 2. Don't Await execute()
134128

135-
```dart
136-
// ✓ GOOD - Non-blocking, UI stays responsive
137-
ElevatedButton(
138-
onPressed: () => di<Manager>().command.execute(),
139-
child: Text('Submit'),
140-
)
141-
142-
// ❌ BAD - Blocks UI thread
143-
ElevatedButton(
144-
onPressed: () async {
145-
await di<Manager>().command.executeWithFuture();
146-
},
147-
child: Text('Submit'),
148-
)
149-
```
129+
<<< @/../code_samples/lib/watch_it/command_observing_patterns.dart#dont_await_execute_good
130+
131+
<<< @/../code_samples/lib/watch_it/command_observing_patterns.dart#dont_await_execute_bad
150132

151133
**Why?** Commands handle async internally. Just call `execute()` and let watch_it update the UI reactively.
152134

153135
### 3. Watch Execution State for Loading
154136

155-
```dart
156-
// ✓ GOOD - Watch isExecuting
157-
final isLoading = watch(command.isExecuting).value;
158-
159-
if (isLoading) {
160-
return CircularProgressIndicator();
161-
}
137+
<<< @/../code_samples/lib/watch_it/command_observing_patterns.dart#watch_execution_state_good
162138

163-
// ❌ BAD - Manual tracking
164-
bool _isLoading = false;
165-
166-
setState(() => _isLoading = true);
167-
await command.executeWithFuture();
168-
setState(() => _isLoading = false);
169-
```
139+
**Avoid manual tracking:** Don't use `setState` and boolean flags. Let commands and watch_it handle state reactively.
170140

171141
### 4. Handle Errors Gracefully
172142

173-
```dart
174-
// ✓ GOOD - Watch errors and display them
175-
final error = watch(command.errors).value;
176-
177-
if (error != null) {
178-
return ErrorWidget(error: error);
179-
}
180-
181-
// ✓ ALSO GOOD - Use handler for error dialog
182-
registerHandler(
183-
select: (Manager m) => m.command.errors,
184-
handler: (context, error, _) {
185-
if (error != null) {
186-
showErrorDialog(context, error);
187-
}
188-
},
189-
);
190-
```
143+
<<< @/../code_samples/lib/watch_it/command_observing_patterns.dart#handle_errors_good_watch
191144

192-
### 5. Only Show Loading on Initial Load
145+
<<< @/../code_samples/lib/watch_it/command_observing_patterns.dart#handle_errors_good_handler
193146

194-
```dart
195-
final isLoading = watch(command.isExecuting).value;
196-
final data = watch(command).value;
197-
198-
// Show spinner only when no data yet
199-
if (isLoading && data == null) {
200-
return CircularProgressIndicator();
201-
}
147+
### 5. Only Show Loading on Initial Load
202148

203-
// Show data even while refreshing
204-
return ListView(
205-
children: [
206-
if (isLoading) LinearProgressIndicator(), // Subtle indicator
207-
...data.map((item) => ListTile(...)),
208-
],
209-
);
210-
```
149+
<<< @/../code_samples/lib/watch_it/command_observing_patterns.dart#initial_load_pattern
211150

212151
## Common Patterns
213152

214153
### Form Submission
215154

216-
```dart
217-
final isSubmitting = watch(manager.submitCommand.isExecuting).value;
218-
final canSubmit = formKey.currentState?.validate() ?? false;
219-
220-
ElevatedButton(
221-
onPressed: canSubmit && !isSubmitting
222-
? () => manager.submitCommand.execute(formData)
223-
: null,
224-
child: isSubmitting
225-
? CircularProgressIndicator()
226-
: Text('Submit'),
227-
)
228-
```
155+
<<< @/../code_samples/lib/watch_it/command_observing_patterns.dart#form_submission_pattern
229156

230157
### Pull to Refresh
231158

232-
```dart
233-
RefreshIndicator(
234-
onRefresh: () async {
235-
manager.refreshCommand.execute();
236-
await manager.refreshCommand.executeWithFuture();
237-
},
238-
child: ListView(...),
239-
)
240-
```
159+
<<< @/../code_samples/lib/watch_it/command_observing_patterns.dart#pull_to_refresh_pattern
241160

242161
### Retry on Error
243162

244-
```dart
245-
final error = watch(command.errors).value;
246-
247-
if (error != null) {
248-
return Column(
249-
children: [
250-
Text('Error: $error'),
251-
ElevatedButton(
252-
onPressed: () => command.execute(),
253-
child: Text('Retry'),
254-
),
255-
],
256-
);
257-
}
258-
```
163+
<<< @/../code_samples/lib/watch_it/command_observing_patterns.dart#retry_on_error_pattern
259164

260165
## See Also
261166

0 commit comments

Comments
 (0)