• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Duit-Foundation / flutter_duit / 21026233449

15 Jan 2026 09:26AM UTC coverage: 80.184% (-8.3%) from 88.441%
21026233449

push

github

web-flow
feat: Capability-based API migration pt2 (#323)

162 of 408 new or added lines in 23 files covered. (39.71%)

328 existing lines in 17 files now uncovered.

4354 of 5430 relevant lines covered (80.18%)

34.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

48.57
/lib/src/capabilities/action_capability_impl.dart
1
import "dart:async";
2

3
import "package:duit_kernel/duit_kernel.dart";
4
import "package:flutter/widgets.dart";
5
import "package:flutter_duit/flutter_duit.dart";
6

7
class DuitActionManager with ServerActionExecutionCapabilityDelegate {
8
  late final UIDriver _driver;
9
  final _dataSources = <int, StreamSubscription<ServerEvent>>{};
10
  UserDefinedEventHandler? _navigationHandler,
11
      _openUrlHandler,
12
      _customEventHandler;
13

14
  Future<ServerEvent?> _executeAction(
44✔
15
    ServerAction action,
16
  ) async {
17
    switch (action) {
18
      //transport
19
      case TransportAction(
44✔
20
          :final dependsOn,
×
21
        ):
22
        try {
23
          final payload = _driver.preparePayload(dependsOn);
×
NEW
24
          final res = await _driver.executeRemoteAction(action, payload);
×
25

26
          if (res != null) {
27
            return ServerEvent.parseEvent(res);
×
28
          }
29
          return null;
30
        } catch (e, s) {
NEW
31
          _driver.logError(
×
32
            "[Error while executing transport action]",
33
            e,
34
            s,
35
          );
36
        }
37
        break;
38
      //local execution
39
      case LocalAction(
44✔
40
          :final event,
44✔
41
        ):
42
        return event;
43
      //script
44
      case ScriptAction(
×
45
          :final dependsOn,
×
46
          :final script,
×
47
          :final eventName,
×
48
        ):
49
        try {
50
          final body = _driver.preparePayload(dependsOn);
×
51

NEW
52
          final scriptInvocationResult = await _driver.execScript(
×
53
            script.functionName,
×
54
            url: eventName,
55
            meta: action.script.meta,
×
56
            body: body,
57
          );
58

59
          if (scriptInvocationResult != null) {
60
            return ServerEvent.parseEvent(scriptInvocationResult);
×
61
          }
62
          return null;
63
        } catch (e, s) {
NEW
64
          _driver.logError(
×
65
            "[Error while executing script action]",
66
            e,
67
            s,
68
          );
69
        }
70
        break;
71
    }
72
    return null;
73
  }
74

75
  @override
4✔
76
  Map<String, dynamic> preparePayload(
77
    Iterable<ActionDependency> dependencies,
78
  ) {
79
    final payload = <String, dynamic>{};
4✔
80

81
    if (dependencies.isNotEmpty) {
4✔
82
      for (final dependency in dependencies) {
8✔
83
        final controller = _driver.getController(dependency.id);
12✔
84
        if (controller != null) {
85
          final attribute = controller.attributes.payload;
8✔
86
          payload[dependency.target] = attribute["value"];
12✔
87
        }
88
      }
89
    }
90
    return payload;
91
  }
92

93
  @override
48✔
94
  Future<void> resolveEvent(BuildContext context, eventData) async {
95
    ServerEvent event;
96

97
    if (eventData is ServerEvent) {
48✔
98
      event = eventData;
99
    } else {
100
      event = ServerEvent.parseEvent(eventData);
×
101
    }
102

103
    try {
104
      switch (event) {
105
        case UpdateEvent(
48✔
106
            :final updates,
36✔
107
          ):
108
          for (final MapEntry(:key, :value) in updates.entries) {
144✔
109
            _driver.updateAttributes(key, value);
72✔
110
          }
111
          break;
112
        case NavigationEvent(
20✔
113
            :final path,
×
114
            :final extra,
×
115
          ):
116
          if (_navigationHandler != null) {
×
117
            await _navigationHandler!(context, path, extra);
×
118
          } else {
NEW
119
            _driver.logWarning(
×
120
              "NavigationEvent received but no handler attached",
121
            );
122
          }
123
          break;
124
        case OpenUrlEvent(
20✔
125
            :final url,
×
126
          ):
127
          if (_openUrlHandler != null) {
×
128
            await _openUrlHandler!(context, url, const {});
×
129
          } else {
NEW
130
            _driver.logWarning(
×
131
              "OpenUrlEvent received but no handler attached",
132
            );
133
          }
134
          break;
135
        case CustomEvent(
20✔
136
            :final key,
4✔
137
            :final extra,
4✔
138
          ):
139
          if (_driver.isModule) {
8✔
NEW
140
            await _driver.invokeNativeMethod<Map<String, dynamic>>(
×
141
              key,
142
              extra,
143
            );
144
          } else {
145
            if (_customEventHandler != null) {
4✔
146
              await _customEventHandler!(context, key, extra);
×
147
            } else {
148
              _driver.logWarning(
8✔
149
                "CustomEvent received but no handler attached",
150
              );
151
            }
152
          }
153
          break;
154
        case SequencedEventGroup(
16✔
155
            :final events,
×
156
          ):
157
          for (final entry in events) {
×
158
            if (context.mounted) {
×
159
              await resolveEvent(context, entry.event);
×
160
              await Future.delayed(entry.delay);
×
161
            }
162
          }
163
          break;
164
        case CommonEventGroup(
16✔
165
            :final events,
×
166
          ):
167
          for (final entry in events) {
×
168
            await resolveEvent(context, entry.event);
×
169
          }
170
          break;
171
        case CommandEvent(
16✔
172
            :final command,
16✔
173
          ):
174
          final c = _driver.getController(command.controllerId);
48✔
175
          await c?.emitCommand(command);
16✔
176
          break;
177
        case TimerEvent(
×
178
            :final timerDelay,
×
179
            :final payload,
×
180
          ):
181
          Future.delayed(
×
182
            timerDelay,
183
            () async {
×
184
              if (context.mounted) {
×
185
                await resolveEvent(context, payload);
×
186
              }
187
            },
188
          );
189
          break;
190
        default:
191
          break;
192
      }
193
    } catch (e, s) {
NEW
194
      _driver.logError(
×
195
        "Error while resolving ${event.type} event",
×
196
        e,
197
        s,
198
      );
199
    }
200
  }
201

202
  @override
44✔
203
  Future<void> execute(ServerAction action) async {
204
    try {
205
      final event = await _executeAction(
44✔
206
        action,
207
      );
208

209
      final ctx = _driver.buildContext;
88✔
210
      if (event != null && ctx.mounted) {
44✔
211
        resolveEvent(ctx, event);
44✔
212
      }
213
    } catch (e, s) {
NEW
214
      _driver.logError(
×
215
        "Error executing action",
216
        e,
217
        s,
218
      );
219
    }
220
  }
221

222
  @override
4✔
223
  void addExternalEventStream(
224
    Stream<Map<String, dynamic>> stream,
225
  ) {
226
    final id = stream.hashCode;
4✔
227

228
    _dataSources[id] = stream.map(ServerEvent.parseEvent).listen(
16✔
229
      (event) {
4✔
230
        if (event is NullEvent) {
4✔
231
          throw const NullEventException("NullEvent received from data source");
232
        }
233
        resolveEvent(
4✔
234
          // ignore: use_build_context_synchronously
235
          _driver.buildContext,
8✔
236
          event,
237
        );
238
      },
239
      onDone: () => _cancelSub(id),
8✔
240
      onError: (e, s) => _cancelSub(id),
×
241
    );
242
  }
243

244
  @preferInline
4✔
245
  void _cancelSub(int code) => _dataSources.remove(code)?.cancel();
12✔
246

247
  @override
4✔
248
  void releaseResources() {
249
    for (final subscription in _dataSources.values) {
8✔
250
      subscription.cancel();
×
251
    }
252
    _dataSources.clear();
8✔
253
  }
254

255
  @override
×
256
  void attachExternalHandler(
257
    UserDefinedHandlerKind type,
258
    UserDefinedEventHandler handle,
259
  ) {
260
    switch (type) {
261
      case UserDefinedHandlerKind.navigation:
×
262
        _navigationHandler = handle;
×
263
        break;
264
      case UserDefinedHandlerKind.openUrl:
×
265
        _openUrlHandler = handle;
×
266
        break;
267
      case UserDefinedHandlerKind.custom:
×
268
        _customEventHandler = handle;
×
269
        break;
270
    }
271
  }
272

273
  @override
348✔
274
  void linkDriver(UIDriver driver) => _driver = driver;
348✔
275
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc