Skip to content

Commit 218269e

Browse files
committed
Improve WatchingStatefulWidget example with better local state pattern
- Replaced SearchableList with NotificationToggle - Shows true local UI state (_isAnimating) for immediate feedback - Demonstrates updating manager (toggleNotifications) from local interaction - Better illustrates when StatefulWidget is actually needed - Local state for UI, reactive state for business logic from manager
1 parent 729ef43 commit 218269e

File tree

2 files changed

+57
-54
lines changed

2 files changed

+57
-54
lines changed

code_samples/lib/watch_it/watching_widgets_patterns.dart

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -16,49 +16,49 @@ class TodoList extends WatchingWidget {
1616
}
1717
// #endregion watching_widget_basic
1818

19-
// Helper class for searchable examples
20-
class Item {
21-
final String name;
22-
Item(this.name);
23-
}
19+
// Helper class for settings example
20+
class SettingsManager {
21+
final notificationsEnabled = ValueNotifier<bool>(true);
22+
final itemCount = ValueNotifier<int>(0);
2423

25-
class Manager {
26-
final items = ValueNotifier<List<Item>>([
27-
Item('Apple'),
28-
Item('Banana'),
29-
Item('Cherry'),
30-
]);
24+
void toggleNotifications(bool enabled) {
25+
notificationsEnabled.value = enabled;
26+
}
3127
}
3228

3329
// #region watching_stateful_widget
34-
class SearchableList extends WatchingStatefulWidget {
30+
class NotificationToggle extends WatchingStatefulWidget {
3531
@override
36-
State createState() => _SearchableListState();
32+
State createState() => _NotificationToggleState();
3733
}
3834

39-
class _SearchableListState extends State<SearchableList> {
40-
String _query = ''; // Local state
35+
class _NotificationToggleState extends State<NotificationToggle> {
36+
bool _isAnimating = false; // Local UI state for animation
4137

4238
@override
4339
Widget build(BuildContext context) {
44-
// Reactive state - automatically rebuilds
45-
final items = watchValue((Manager m) => m.items);
46-
47-
// Filter using local state
48-
final filtered = items.where((item) => item.name.contains(_query)).toList();
49-
50-
return Column(
51-
children: [
52-
TextField(
53-
onChanged: (value) => setState(() => _query = value),
54-
),
55-
Expanded(
56-
child: ListView.builder(
57-
itemCount: filtered.length,
58-
itemBuilder: (context, index) => Text(filtered[index].name),
59-
),
60-
),
61-
],
40+
// Reactive state - automatically rebuilds when manager changes
41+
final enabled = watchValue((SettingsManager m) => m.notificationsEnabled);
42+
43+
return SwitchListTile(
44+
title: Text('Notifications'),
45+
subtitle: _isAnimating ? Text('Updating...') : null,
46+
value: enabled,
47+
onChanged: (value) async {
48+
// Update local state for immediate UI feedback
49+
setState(() => _isAnimating = true);
50+
51+
// Update manager (business logic)
52+
di<SettingsManager>().toggleNotifications(value);
53+
54+
// Simulate async operation (API call, etc.)
55+
await Future.delayed(Duration(milliseconds: 500));
56+
57+
// Clear local animation state
58+
if (mounted) {
59+
setState(() => _isAnimating = false);
60+
}
61+
},
6262
);
6363
}
6464
}
@@ -80,34 +80,34 @@ class TodoListWithMixin extends StatelessWidget with WatchItMixin {
8080
// #endregion mixin_stateless
8181

8282
// #region mixin_stateful
83-
class SearchableListWithMixin extends StatefulWidget
83+
class NotificationToggleWithMixin extends StatefulWidget
8484
with WatchItStatefulWidgetMixin {
85-
const SearchableListWithMixin({super.key});
85+
const NotificationToggleWithMixin({super.key});
8686

8787
@override
88-
State createState() => _SearchableListWithMixinState();
88+
State createState() => _NotificationToggleWithMixinState();
8989
}
9090

91-
class _SearchableListWithMixinState extends State<SearchableListWithMixin> {
92-
String _query = '';
91+
class _NotificationToggleWithMixinState
92+
extends State<NotificationToggleWithMixin> {
93+
bool _isAnimating = false;
9394

9495
@override
9596
Widget build(BuildContext context) {
96-
final items = watchValue((Manager m) => m.items);
97-
final filtered = items.where((item) => item.name.contains(_query)).toList();
98-
99-
return Column(
100-
children: [
101-
TextField(
102-
onChanged: (value) => setState(() => _query = value),
103-
),
104-
Expanded(
105-
child: ListView.builder(
106-
itemCount: filtered.length,
107-
itemBuilder: (context, index) => Text(filtered[index].name),
108-
),
109-
),
110-
],
97+
final enabled = watchValue((SettingsManager m) => m.notificationsEnabled);
98+
99+
return SwitchListTile(
100+
title: Text('Notifications'),
101+
subtitle: _isAnimating ? Text('Updating...') : null,
102+
value: enabled,
103+
onChanged: (value) async {
104+
setState(() => _isAnimating = true);
105+
di<SettingsManager>().toggleNotifications(value);
106+
await Future.delayed(Duration(milliseconds: 500));
107+
if (mounted) {
108+
setState(() => _isAnimating = false);
109+
}
110+
},
111111
);
112112
}
113113
}

docs/documentation/watch_it/watching_widgets.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@ Use when you need both `setState` AND reactive state:
2929
<<< @/../code_samples/lib/watch_it/watching_widgets_patterns.dart#watching_stateful_widget
3030

3131
**Use this when:**
32-
- You need local UI state (search queries, form input, expansion state)
32+
- You need local UI state (form input state, animation flags, expansion state)
3333
- You need animation controllers
3434
- Mix `setState` with reactive updates
35+
- Local state triggers manager updates (like this switch example)
3536

3637
**Note:** Your State class automatically gets all watch functions - no mixin needed!
3738

39+
**Pattern:** Local state (`_isAnimating`) provides immediate UI feedback, while reactive state (`notificationsEnabled`) ensures the widget reflects manager changes from anywhere in the app.
40+
3841
## Alternative: Using Mixins
3942

4043
If you have **existing widgets** you don't want to change, use mixins instead:

0 commit comments

Comments
 (0)