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

Duit-Foundation / flutter_duit / 19115039532

05 Nov 2025 08:15PM UTC coverage: 86.358% (+8.9%) from 77.409%
19115039532

push

github

web-flow
major: flutter_duit v4 (#310)

2151 of 2405 new or added lines in 109 files covered. (89.44%)

36 existing lines in 7 files now uncovered.

3773 of 4369 relevant lines covered (86.36%)

35.87 hits per line

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

79.59
/lib/src/controller/commands.dart
1
import 'package:duit_kernel/duit_kernel.dart';
2
import 'package:flutter/material.dart';
3
import 'package:flutter_duit/src/controller/data.dart';
4
import 'package:flutter_duit/src/utils/index.dart';
5

6
/// A lookup table that maps command types to their corresponding factory constructors.
7
///
8
/// This constant map associates string identifiers with functions that can create
9
/// specialized command instances from generic [RemoteCommand] objects. Each entry
10
/// maps a command type string to a factory function that knows how to parse
11
/// the payload and create the appropriate command subclass.
12
///
13
/// Currently supports:
14
/// - `"animation"`: Creates [AnimationCommand] instances for animation control
15
const _commandLookup = <String, RemoteCommand Function(RemoteCommand)>{
16
  "animation": AnimationCommand.fromRemoteCommand,
17
  "bottomSheet": BottomSheetCommand.fromRemoteCommand,
18
  "dialog": DialogCommand.fromRemoteCommand,
19
};
20

21
/// An extension type that provides command specification functionality.
22
///
23
/// This extension type wraps a [RemoteCommand] and provides a method to convert
24
/// it into a specialized command instance based on its type. It uses the
25
/// [_commandLookup] table to find the appropriate factory constructor for
26
/// the command type.
27
extension type SpecCommand(RemoteCommand command) {
28
  /// Converts the generic [RemoteCommand] into a specialized command instance.
29
  ///
30
  /// This method looks up the command type in the [_commandLookup] table and
31
  /// uses the corresponding factory constructor to create a properly typed
32
  /// command instance. If the command type is not found in the lookup table,
33
  /// it throws an [ArgumentError] with details about the unknown command type.
34
  ///
35
  /// Returns a specialized command instance that corresponds to the original
36
  /// command's type.
37
  ///
38
  /// Throws an [ArgumentError] if the command type is not supported.
39
  @preferInline
8✔
40
  RemoteCommand specify() {
41
    final type = command.commandData["type"] as String;
16✔
42
    final ctor = _commandLookup[type];
4✔
43
    if (ctor == null) {
NEW
44
      throw Exception("Unknown command type: $type");
×
45
    }
46

47
    return ctor(command);
4✔
48
  }
49
}
50

51
/// A command that controls animation behavior for a specific animated property.
52
///
53
/// This command extends [RemoteCommand] to provide animation-specific functionality.
54
/// It contains information about which animated property to control and what
55
/// animation method to apply.
56
final class AnimationCommand extends RemoteCommand {
57
  /// The key identifying the animated property to be controlled.
58
  ///
59
  /// This key is used to match the animation with the corresponding
60
  /// animated property in the animation system.
61
  final String animatedPropKey;
62

63
  /// The animation method to be executed.
64
  ///
65
  /// Defines what action should be performed on the animation,
66
  /// such as play, pause, reverse, etc.
67
  final AnimationMethod method;
68

69
  final AnimationTrigger trigger;
70

71
  /// Creates an [AnimationCommand] with the specified parameters.
72
  ///
73
  /// All parameters are required to ensure the command has complete
74
  /// information for animation control.
NEW
75
  const AnimationCommand({
×
76
    required super.commandData,
77
    required super.controllerId,
78
    required super.type,
79
    required this.animatedPropKey,
80
    required this.method,
81
    required this.trigger,
82
  });
83

84
  /// Creates an [AnimationCommand] from a generic [RemoteCommand].
85
  ///
86
  /// This factory constructor extracts animation-specific data from the
87
  /// payload of a [RemoteCommand] and creates a properly typed
88
  /// [AnimationCommand] instance.
89
  ///
90
  /// The payload is expected to contain:
91
  /// - `animatedPropKey`: A string identifying the animated property
92
  /// - Animation method information that can be parsed by [DuitDataSource.animationMethod]
93
  ///
94
  /// Throws if the payload doesn't contain the required animation data.
NEW
95
  factory AnimationCommand.fromRemoteCommand(RemoteCommand command) {
×
NEW
96
    final source = DuitDataSource(command.commandData);
×
NEW
97
    return AnimationCommand(
×
NEW
98
      trigger: source.animationTrigger(),
×
NEW
99
      animatedPropKey: source.getString(key: "animatedPropKey"),
×
NEW
100
      method: source.animationMethod(),
×
NEW
101
      controllerId: command.controllerId,
×
102
      type: "animation",
NEW
103
      commandData: command.commandData,
×
104
    );
105
  }
106
}
107

108
/// A command representing the configuration and action for displaying or closing a BottomSheet.
109
///
110
/// This command is typically sent from the remote layer to instruct the UI to show or hide
111
/// a modal bottom sheet with the specified content and options. It encapsulates all the
112
/// parameters required to configure the appearance and behavior of the BottomSheet, such as
113
/// scroll control, background color, shape, and more.
114
///
115
/// The [action] property determines whether the BottomSheet should be opened or closed.
116
/// The [onClose] callback can be used to trigger an action when the BottomSheet is dismissed.
117
final class BottomSheetCommand extends RemoteCommand {
118
  // showModalBottomSheet properties
119
  final bool isScrollControlled,
120
      isDismissible,
121
      useSafeArea,
122
      useRootNavigator,
123
      enableDrag;
124
  final bool? showDragHandle;
125
  final double scrollControlDisabledMaxHeightRatio;
126
  final BoxConstraints? constraints;
127
  final Offset? anchorPoint;
128
  final Color? backgroundColor, barrierColor;
129
  final ShapeBorder? shape;
130
  final Clip? clipBehavior;
131
  // Specific duit properties
132
  final Map<String, dynamic> content;
133
  final ServerAction? onClose;
134
  final OverlayAction action;
135

136
  const BottomSheetCommand({
4✔
137
    required super.commandData,
138
    required super.controllerId,
139
    required super.type,
140
    required this.content,
141
    required this.isScrollControlled,
142
    required this.isDismissible,
143
    required this.useSafeArea,
144
    required this.useRootNavigator,
145
    required this.enableDrag,
146
    required this.showDragHandle,
147
    required this.scrollControlDisabledMaxHeightRatio,
148
    required this.backgroundColor,
149
    required this.barrierColor,
150
    required this.shape,
151
    required this.clipBehavior,
152
    required this.constraints,
153
    required this.anchorPoint,
154
    required this.onClose,
155
    required this.action,
156
  });
157

158
  factory BottomSheetCommand.fromRemoteCommand(RemoteCommand command) {
4✔
159
    final source = DuitDataSource(command.commandData);
8✔
160
    return BottomSheetCommand(
4✔
161
      content: source["content"] ?? const {},
4✔
162
      controllerId: overlayTriggerId,
163
      type: "bottomSheet",
164
      commandData: command.commandData,
4✔
165
      isScrollControlled: source.getBool("isScrollControlled"),
4✔
166
      backgroundColor: source.tryParseColor(key: "backgroundColor"),
4✔
167
      barrierColor: source.tryParseColor(key: "barrierColor"),
4✔
168
      shape: source.shapeBorder(key: "shape"),
4✔
169
      clipBehavior: source.clipBehavior(key: "clipBehavior"),
4✔
170
      useSafeArea: source.getBool("useSafeArea"),
4✔
171
      useRootNavigator: source.getBool("useRootNavigator"),
4✔
172
      isDismissible: source.getBool(
4✔
173
        "isDismissible",
174
        defaultValue: true,
175
      ),
176
      enableDrag: source.getBool(
4✔
177
        "enableDrag",
178
        defaultValue: true,
179
      ),
180
      showDragHandle: source.tryGetBool("showDragHandle"),
4✔
181
      constraints: source.boxConstraints(key: "constraints"),
4✔
182
      anchorPoint: source.offset(key: "anchorPoint"),
4✔
183
      scrollControlDisabledMaxHeightRatio: source.getDouble(
4✔
184
        key: "scrollControlDisabledMaxHeightRatio",
185
        defaultValue: defaultScrollControlDisabledMaxHeightRatio,
186
      ),
187
      onClose: source.getAction("onClose"),
4✔
188
      action: OverlayAction.parse(source.getString(key: 'action')),
8✔
189
      // transitionAnimationController not supported
190
      // sheetAnimationStyle not supported
191
    );
192
  }
193
}
194

195
final class DialogCommand extends RemoteCommand {
196
  final bool barrierDismissible, useSafeArea, useRootNavigator;
197
  final Color? barrierColor;
198
  final String? barrierLabel;
199
  final Offset? anchorPoint;
200
  // Specific duit properties
201
  final Map<String, dynamic> content;
202
  final ServerAction? onClose;
203
  final OverlayAction action;
204

205
  const DialogCommand({
4✔
206
    required super.commandData,
207
    required super.controllerId,
208
    required super.type,
209
    required this.barrierDismissible,
210
    required this.useSafeArea,
211
    required this.useRootNavigator,
212
    required this.barrierColor,
213
    required this.barrierLabel,
214
    required this.anchorPoint,
215
    required this.content,
216
    required this.onClose,
217
    required this.action,
218
  });
219

220
  factory DialogCommand.fromRemoteCommand(RemoteCommand command) {
4✔
221
    final source = DuitDataSource(command.commandData);
8✔
222
    return DialogCommand(
4✔
223
      content: source["content"] ?? const {},
4✔
224
      commandData: command.commandData,
4✔
225
      controllerId: overlayTriggerId,
226
      type: "dialog",
227
      barrierDismissible: source.getBool(
4✔
228
        "barrierDismissible",
229
        defaultValue: true,
230
      ),
231
      useSafeArea: source.getBool(
4✔
232
        "useSafeArea",
233
        defaultValue: true,
234
      ),
235
      useRootNavigator: source.getBool(
4✔
236
        "useRootNavigator",
237
        defaultValue: true,
238
      ),
239
      barrierColor: source.tryParseColor(key: "barrierColor"),
4✔
240
      barrierLabel: source.getString(key: "barrierLabel"),
4✔
241
      anchorPoint: source.offset(key: "anchorPoint"),
4✔
242
      onClose: source.getAction("onClose"),
4✔
243
      action: OverlayAction.parse(source.getString(key: 'action')),
8✔
244
      // transitionAnimationController not supported
245
      // sheetAnimationStyle not supported
246
    );
247
  }
248
}
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