Skip to content

Commit 5cbf0ed

Browse files
committed
Add pipeToCommand documentation (Command Chaining)
- Create command_chaining.md documenting the new pipeToCommand extension - Add Spanish translation (command_chaining.md in es/) - Update VitePress sidebar for both EN and ES (fix ES links to /es/ paths) - Move sync input operators section from best_practices to command_chaining - Add code samples: basic, transform, operators, cleanup patterns
1 parent 26c4f37 commit 5cbf0ed

File tree

9 files changed

+774
-34
lines changed

9 files changed

+774
-34
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import 'package:command_it/command_it.dart';
2+
import 'package:flutter/foundation.dart';
3+
import '_shared/stubs.dart';
4+
5+
final api = ApiClient();
6+
7+
// #region basic
8+
class DataManager {
9+
// When saveCommand completes, automatically refresh
10+
late final saveCommand = Command.createAsyncNoResult<Data>(
11+
(data) => api.save(data),
12+
);
13+
14+
late final refreshCommand = Command.createAsyncNoParam<List<Data>>(
15+
() => api.fetchData(),
16+
initialValue: [],
17+
);
18+
19+
DataManager() {
20+
// Pipe save results to trigger refresh
21+
saveCommand.pipeToCommand(refreshCommand);
22+
}
23+
24+
void dispose() {
25+
saveCommand.dispose();
26+
refreshCommand.dispose();
27+
}
28+
}
29+
// #endregion basic
30+
31+
// #region from_isrunning
32+
class SpinnerManager {
33+
late final longRunningCommand = Command.createAsyncNoParam<Data>(
34+
() async {
35+
await Future.delayed(Duration(seconds: 5));
36+
return Data();
37+
},
38+
initialValue: Data.empty(),
39+
);
40+
41+
// Command that controls a global spinner
42+
late final showSpinnerCommand = Command.createSync<bool, bool>(
43+
(show) => show,
44+
initialValue: false,
45+
);
46+
47+
SpinnerManager() {
48+
// When long command starts/stops, update spinner
49+
longRunningCommand.isRunning.pipeToCommand(showSpinnerCommand);
50+
}
51+
}
52+
// #endregion from_isrunning
53+
54+
// #region from_results
55+
class LoggingManager {
56+
late final saveCommand = Command.createAsync<Data, Data>(
57+
(data) async {
58+
await api.save(data);
59+
return data;
60+
},
61+
initialValue: Data.empty(),
62+
);
63+
64+
late final logCommand = Command.createSync<CommandResult<Data, Data>, void>(
65+
(result) {
66+
if (result.hasError) {
67+
debugPrint('Save failed: ${result.error}');
68+
} else if (result.hasData) {
69+
debugPrint('Save succeeded: ${result.data}');
70+
}
71+
},
72+
initialValue: null,
73+
);
74+
75+
LoggingManager() {
76+
// Pipe all results (success/error) to logging
77+
saveCommand.results.pipeToCommand(logCommand);
78+
}
79+
}
80+
// #endregion from_results
81+
82+
// #region from_valuenotifier
83+
class FormManager {
84+
final selectedUserId = ValueNotifier<String>('');
85+
86+
late final loadUserCommand = Command.createAsync<String, User>(
87+
(userId) => api.login(userId, ''),
88+
initialValue: User.empty(),
89+
);
90+
91+
FormManager() {
92+
// When user ID changes, load user details
93+
selectedUserId.pipeToCommand(loadUserCommand);
94+
}
95+
96+
void dispose() {
97+
selectedUserId.dispose();
98+
loadUserCommand.dispose();
99+
}
100+
}
101+
// #endregion from_valuenotifier
102+
103+
void main() {
104+
// Examples compile but don't run
105+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import 'package:command_it/command_it.dart';
2+
import '_shared/stubs.dart';
3+
4+
final api = ApiClient();
5+
6+
// #region cleanup_basic
7+
class CleanupManager {
8+
late final sourceCommand = Command.createSync<String, String>(
9+
(s) => s,
10+
initialValue: '',
11+
);
12+
13+
late final targetCommand = Command.createAsyncNoResult<String>(
14+
(s) async => api.saveContent(s),
15+
);
16+
17+
// Store subscription for cleanup
18+
late final ListenableSubscription _subscription;
19+
20+
CleanupManager() {
21+
_subscription = sourceCommand.pipeToCommand(targetCommand);
22+
}
23+
24+
void dispose() {
25+
// Cancel subscription first
26+
_subscription.cancel();
27+
// Then dispose commands
28+
sourceCommand.dispose();
29+
targetCommand.dispose();
30+
}
31+
}
32+
// #endregion cleanup_basic
33+
34+
// #region cleanup_multiple
35+
class MultiPipeManager {
36+
late final inputCommand = Command.createSync<String, String>(
37+
(s) => s,
38+
initialValue: '',
39+
);
40+
41+
late final saveCommand = Command.createAsyncNoResult<String>(
42+
(s) async => api.saveContent(s),
43+
);
44+
45+
late final logCommand = Command.createSync<String, void>(
46+
(s) => print('Logged: $s'),
47+
initialValue: null,
48+
);
49+
50+
late final analyticsCommand = Command.createSync<String, void>(
51+
(s) => print('Analytics: $s'),
52+
initialValue: null,
53+
);
54+
55+
// Multiple subscriptions
56+
final List<ListenableSubscription> _subscriptions = [];
57+
58+
MultiPipeManager() {
59+
_subscriptions.addAll([
60+
inputCommand.pipeToCommand(saveCommand),
61+
inputCommand.pipeToCommand(logCommand),
62+
inputCommand.pipeToCommand(analyticsCommand),
63+
]);
64+
}
65+
66+
void dispose() {
67+
// Cancel all subscriptions
68+
for (final sub in _subscriptions) {
69+
sub.cancel();
70+
}
71+
_subscriptions.clear();
72+
73+
inputCommand.dispose();
74+
saveCommand.dispose();
75+
logCommand.dispose();
76+
analyticsCommand.dispose();
77+
}
78+
}
79+
// #endregion cleanup_multiple
80+
81+
// #region cleanup_conditional
82+
class ConditionalPipeManager {
83+
late final sourceCommand = Command.createSync<String, String>(
84+
(s) => s,
85+
initialValue: '',
86+
);
87+
88+
late final targetCommand = Command.createAsyncNoResult<String>(
89+
(s) async => api.saveContent(s),
90+
);
91+
92+
ListenableSubscription? _subscription;
93+
94+
void enablePipe() {
95+
// Only create if not already active
96+
_subscription ??= sourceCommand.pipeToCommand(targetCommand);
97+
}
98+
99+
void disablePipe() {
100+
_subscription?.cancel();
101+
_subscription = null;
102+
}
103+
104+
void dispose() {
105+
_subscription?.cancel();
106+
sourceCommand.dispose();
107+
targetCommand.dispose();
108+
}
109+
}
110+
// #endregion cleanup_conditional
111+
112+
void main() {
113+
// Examples compile but don't run
114+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import 'package:command_it/command_it.dart';
2+
import '_shared/stubs.dart';
3+
4+
final api = ApiClient();
5+
6+
// #region search_example
7+
class SearchManager {
8+
late final textChangedCommand = Command.createSync<String, String>(
9+
(s) => s,
10+
initialValue: '',
11+
);
12+
13+
late final searchCommand = Command.createAsync<String, List<Result>>(
14+
(query) => api.search(query),
15+
initialValue: [],
16+
);
17+
18+
late final ListenableSubscription _subscription;
19+
20+
SearchManager() {
21+
// Debounce + filter + pipe to search command
22+
_subscription = textChangedCommand
23+
.debounce(Duration(milliseconds: 500))
24+
.where((text) => text.length >= 3)
25+
.pipeToCommand(searchCommand);
26+
}
27+
28+
void dispose() {
29+
_subscription.cancel();
30+
textChangedCommand.dispose();
31+
searchCommand.dispose();
32+
}
33+
}
34+
// #endregion search_example
35+
36+
// #region filter_example
37+
class FilteredPipeManager {
38+
late final inputCommand = Command.createSync<int, int>(
39+
(n) => n,
40+
initialValue: 0,
41+
);
42+
43+
late final processCommand = Command.createAsync<int, String>(
44+
(n) async => 'Processed: $n',
45+
initialValue: '',
46+
);
47+
48+
late final ListenableSubscription _subscription;
49+
50+
FilteredPipeManager() {
51+
// Only pipe positive numbers, debounced
52+
_subscription = inputCommand
53+
.where((n) => n > 0)
54+
.debounce(Duration(milliseconds: 200))
55+
.pipeToCommand(processCommand);
56+
}
57+
58+
void dispose() {
59+
_subscription.cancel();
60+
inputCommand.dispose();
61+
processCommand.dispose();
62+
}
63+
}
64+
// #endregion filter_example
65+
66+
// #region map_example
67+
class MappedPipeManager {
68+
late final rawInputCommand = Command.createSync<String, String>(
69+
(s) => s,
70+
initialValue: '',
71+
);
72+
73+
late final processCommand = Command.createAsync<String, List<Result>>(
74+
(query) => api.search(query),
75+
initialValue: [],
76+
);
77+
78+
late final ListenableSubscription _subscription;
79+
80+
MappedPipeManager() {
81+
// Normalize input before piping
82+
_subscription = rawInputCommand
83+
.map((s) => s.trim().toLowerCase())
84+
.where((s) => s.isNotEmpty)
85+
.debounce(Duration(milliseconds: 300))
86+
.pipeToCommand(processCommand);
87+
}
88+
89+
void dispose() {
90+
_subscription.cancel();
91+
rawInputCommand.dispose();
92+
processCommand.dispose();
93+
}
94+
}
95+
// #endregion map_example
96+
97+
void main() {
98+
// Examples compile but don't run
99+
}

0 commit comments

Comments
 (0)