Skip to content

Commit 0d443bb

Browse files
committed
Improve troubleshooting docs and format package names consistently
- Fix troubleshooting.md: remove incorrect sections, add correct examples - Extract all inline code examples to separate .dart files for compilation - Format package names (watch_it, get_it, command_it) as code in markdown - Use <code> tags for package names inside HTML elements - Add Bloc/Cubit to Quick Navigation in without_watch_it.md - Add notifyOnlyWhenValueChanges and includeLastResultInCommandResults docs
1 parent 3bd1f59 commit 0d443bb

27 files changed

+1796
-1205
lines changed

code_samples/lib/command_it/_shared/stubs.dart

Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ class UnauthorizedException implements Exception {
5555
String toString() => 'UnauthorizedException: $message';
5656
}
5757

58-
// Toast/Snackbar service for error examples
59-
class ToastService {
58+
// Toast/Snackbar manager for error examples
59+
class ToastManager {
6060
void showError(String message) {
6161
debugPrint('Toast: $message');
6262
}
@@ -70,8 +70,8 @@ class ToastService {
7070
}
7171
}
7272

73-
// Analytics service for error tracking examples
74-
class AnalyticsService {
73+
// Analytics manager for error tracking examples
74+
class AnalyticsManager {
7575
void logError(Object error, StackTrace? stackTrace) {
7676
debugPrint('Analytics: Error logged - $error');
7777
}
@@ -81,6 +81,55 @@ class AnalyticsService {
8181
}
8282
}
8383

84+
// Database stub
85+
class Database {
86+
Future<void> initialize() async {}
87+
}
88+
89+
// User class for auth examples
90+
class User {
91+
final String id;
92+
final String name;
93+
94+
User(this.id, this.name);
95+
96+
static User empty() => User('', '');
97+
}
98+
99+
// Profile class for examples
100+
class Profile {
101+
final String name;
102+
103+
Profile(this.name);
104+
105+
static Profile empty() => Profile('');
106+
}
107+
108+
// Generic Data class for examples
109+
class Data {
110+
final String? id;
111+
final String? value;
112+
113+
Data([this.id, this.value]);
114+
115+
static Data empty() => Data();
116+
}
117+
118+
// Form data for examples
119+
class FormData {
120+
final Map<String, dynamic> fields;
121+
122+
FormData([this.fields = const {}]);
123+
}
124+
125+
// Search result
126+
class Result {
127+
final String id;
128+
final String title;
129+
130+
Result(this.id, this.title);
131+
}
132+
84133
// Additional models for command examples
85134
class Todo {
86135
final String id;
@@ -134,6 +183,38 @@ Future<void> simulateDelay([int milliseconds = 500]) {
134183

135184
// Extension on ApiClient for command_it examples
136185
extension ApiClientCommandExtensions on ApiClient {
186+
Future<List<Data>> fetchData() async {
187+
await simulateDelay();
188+
return [Data('1', 'value1'), Data('2', 'value2')];
189+
}
190+
191+
Future<void> save(Data data) async {
192+
await simulateDelay();
193+
}
194+
195+
Future<void> submit(FormData data) async {
196+
await simulateDelay();
197+
}
198+
199+
Future<List<Result>> search(String query) async {
200+
await simulateDelay();
201+
return [Result('1', 'Result for $query')];
202+
}
203+
204+
Future<User> login(String username, String password) async {
205+
await simulateDelay();
206+
return User('1', username);
207+
}
208+
209+
Future<void> logout() async {
210+
await simulateDelay();
211+
}
212+
213+
Future<Profile> loadProfile() async {
214+
await simulateDelay();
215+
return Profile('John Doe');
216+
}
217+
137218
Future<List<Todo>> fetchTodos() async {
138219
await simulateDelay();
139220
return fakeTodos;
@@ -173,3 +254,31 @@ extension ApiClientCommandExtensions on ApiClient {
173254
void showSnackBar(String message) {
174255
debugPrint('SnackBar: $message');
175256
}
257+
258+
// Progress Control stubs
259+
class Item {
260+
final String? id;
261+
final String? name;
262+
263+
Item([this.id, this.name]);
264+
}
265+
266+
Future<void> uploadChunk(dynamic file, int chunkIndex) async {
267+
await simulateDelay(50);
268+
}
269+
270+
Future<void> downloadData() async {
271+
await simulateDelay(300);
272+
}
273+
274+
Future<void> processData() async {
275+
await simulateDelay(300);
276+
}
277+
278+
Future<void> saveResults() async {
279+
await simulateDelay(300);
280+
}
281+
282+
Future<void> processItem(Item item) async {
283+
await simulateDelay(100);
284+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// ignore_for_file: unused_local_variable, unused_field
2+
import 'package:command_it/command_it.dart';
3+
import 'package:flutter/material.dart';
4+
import '_shared/stubs.dart';
5+
6+
final api = ApiClient();
7+
8+
// #region not_listening_errors
9+
// ❌ BAD: Errors go nowhere
10+
late final commandNoListener = Command.createAsyncNoParam<Data>(
11+
() => api.fetchData().then((list) => list.first),
12+
initialValue: Data.empty(),
13+
errorFilter: const LocalErrorFilter(),
14+
);
15+
// No error listener! Assertions in debug mode
16+
17+
// ✅ GOOD: Always listen to errors when using localHandler
18+
void setupGoodErrorListener() {
19+
commandNoListener.errors.listen((error, _) {
20+
if (error != null) showError(error.error);
21+
});
22+
}
23+
24+
void showError(Object error) {
25+
debugPrint('Error: $error');
26+
}
27+
// #endregion not_listening_errors
28+
29+
// #region try_catch_inside
30+
// ❌ BAD: try/catch inside command function
31+
class DataManagerBad {
32+
late final loadCommand = Command.createAsyncNoParam<Data>(
33+
() async {
34+
try {
35+
final list = await api.fetchData();
36+
return list.first;
37+
} catch (e) {
38+
// Manual error handling defeats command_it's error system
39+
debugPrint('Error: $e');
40+
rethrow;
41+
}
42+
},
43+
initialValue: Data.empty(),
44+
);
45+
}
46+
47+
// ✅ GOOD: Let command handle errors, use ..errors.listen()
48+
class DataManagerGood {
49+
late final loadCommand = Command.createAsyncNoParam<Data>(
50+
() async {
51+
final list = await api.fetchData();
52+
return list.first;
53+
},
54+
initialValue: Data.empty(),
55+
)..errors.listen((error, _) {
56+
if (error != null) {
57+
debugPrint('Error: ${error.error}');
58+
}
59+
});
60+
}
61+
// #endregion try_catch_inside
62+
63+
// #region excessive_results
64+
class ExcessiveResultsExample extends StatelessWidget {
65+
final Command<void, String> command;
66+
67+
const ExcessiveResultsExample({super.key, required this.command});
68+
69+
@override
70+
Widget build(BuildContext context) {
71+
return Column(
72+
children: [
73+
// ❌ BAD: Always using .results when not needed
74+
ValueListenableBuilder(
75+
valueListenable: command.results,
76+
builder: (context, result, _) {
77+
return Text(result.data?.toString() ?? '');
78+
},
79+
),
80+
// Rebuilds on running, error, success
81+
82+
// ✅ GOOD: Use .value for data-only updates
83+
ValueListenableBuilder(
84+
valueListenable: command,
85+
builder: (context, data, _) {
86+
return Text(data.toString());
87+
},
88+
),
89+
// Only rebuilds on successful completion
90+
],
91+
);
92+
}
93+
}
94+
// #endregion excessive_results
95+
96+
// #region forgetting_initial
97+
// ❌ WRONG: Missing initialValue would be compile error
98+
// late final commandMissing = Command.createAsyncNoParam<String>(
99+
// () => api.load(),
100+
// // Missing initialValue!
101+
// );
102+
103+
// ✅ CORRECT: Always provide initialValue
104+
late final commandWithInitial = Command.createAsyncNoParam<String>(
105+
() async => 'loaded',
106+
initialValue: '', // Required for non-void results
107+
);
108+
// #endregion forgetting_initial
109+
110+
// #region sync_isrunning
111+
// ❌ WRONG: Sync commands don't have meaningful isRunning
112+
final syncCommand = Command.createSyncNoParam<String>(
113+
() => 'result',
114+
initialValue: '',
115+
);
116+
117+
// Accessing isRunning on sync command - always false
118+
// ValueListenableBuilder(
119+
// valueListenable: syncCommand.isRunning, // Not useful!
120+
// builder: ...,
121+
// );
122+
123+
// ✅ CORRECT: Use async command if you need isRunning
124+
final asyncCommand = Command.createAsyncNoParam<String>(
125+
() async => 'result',
126+
initialValue: '',
127+
);
128+
// #endregion sync_isrunning
129+
130+
void main() {
131+
setupGoodErrorListener();
132+
}

0 commit comments

Comments
 (0)