1
- import 'dart:developer' ;
2
-
3
1
import 'package:ensemble/framework/action.dart' ;
4
2
import 'package:ensemble/framework/data_context.dart' ;
5
3
import 'package:ensemble/framework/error_handling.dart' ;
6
4
import 'package:ensemble/framework/event.dart' ;
7
- import 'package:ensemble/framework/extensions .dart' ;
5
+ import 'package:ensemble/framework/model .dart' ;
8
6
import 'package:ensemble/framework/scope.dart' ;
9
- import 'package:ensemble/framework/view/context_scope_widget.dart' ;
10
7
import 'package:ensemble/framework/view/data_scope_widget.dart' ;
11
8
import 'package:ensemble/screen_controller.dart' ;
12
9
import 'package:ensemble/util/utils.dart' ;
@@ -15,77 +12,20 @@ import 'package:flutter/material.dart';
15
12
16
13
/// open a Modal Bottom Sheet
17
14
class ShowBottomModalAction extends EnsembleAction {
18
- ShowBottomModalAction (
19
- {super .initiator,
20
- super .inputs,
21
- this .body,
22
- this .styles,
23
- this .options,
24
- this .onDismiss});
15
+ ShowBottomModalAction ({
16
+ super .initiator,
17
+ super .inputs,
18
+ required this .body,
19
+ required this .payload,
20
+ this .onDismiss,
21
+ });
22
+
23
+ static const defaultTopBorderRadius = Radius .circular (16 );
25
24
25
+ final Map payload;
26
26
final dynamic body;
27
- final Map <String , dynamic >? styles;
28
- final Map <String , dynamic >? options;
29
27
final EnsembleAction ? onDismiss;
30
28
31
- // default height is size to content
32
- MainAxisSize getVerticalSize (scopeManager) =>
33
- MainAxisSize .values
34
- .from (scopeManager.dataContext.eval (styles? ['verticalSize' ])) ??
35
- MainAxisSize .min;
36
-
37
- MainAxisAlignment getVerticalAlignment (scopeManager) {
38
- var alignment = scopeManager.dataContext.eval (styles? ['verticalAlignment' ]);
39
- switch (alignment) {
40
- case 'top' :
41
- return MainAxisAlignment .start;
42
- case 'bottom' :
43
- return MainAxisAlignment .end;
44
- default :
45
- // if verticalSize is min this doesn't matter, but center align if max
46
- return MainAxisAlignment .center;
47
- }
48
- }
49
-
50
- // default width is stretching 100%
51
- CrossAxisAlignment getHorizontalSize (scopeManager) {
52
- var size = MainAxisSize .values
53
- .from (scopeManager.dataContext.eval (styles? ['horizontalSize' ])) ??
54
- MainAxisSize .max;
55
- return size == MainAxisSize .min
56
- ? CrossAxisAlignment .center
57
- : CrossAxisAlignment .stretch;
58
- }
59
-
60
- Alignment ? getHorizontalAlignment (scopeManager) {
61
- var alignment =
62
- scopeManager.dataContext.eval (styles? ['horizontalAlignment' ]);
63
- switch (alignment) {
64
- case 'start' :
65
- return Alignment .centerLeft;
66
- case 'center' :
67
- return Alignment .center;
68
- case 'end' :
69
- return Alignment .centerRight;
70
- default :
71
- return null ;
72
- }
73
- }
74
-
75
- Color ? getBarrierColor (scopeManager) =>
76
- Utils .getColor (scopeManager.dataContext.eval (styles? ['barrierColor' ]));
77
-
78
- // default background is the dialog background
79
- Color ? getBackgroundColor (scopeManager) =>
80
- Utils .getColor (scopeManager.dataContext.eval (styles? ['backgroundColor' ]));
81
-
82
- int ? getTopBorderRadius (scopeManager) => Utils .optionalInt (
83
- scopeManager.dataContext.eval (styles? ['topBorderRadius' ]));
84
-
85
- bool getEnableDrag (scopeManager) =>
86
- Utils .getBool (scopeManager.dataContext.eval (styles? ['enableDrag' ]),
87
- fallback: true );
88
-
89
29
factory ShowBottomModalAction .from ({Invokable ? initiator, Map ? payload}) {
90
30
dynamic body = payload? ['body' ] ?? payload? ['widget' ];
91
31
if (payload == null || body == null ) {
@@ -96,86 +36,101 @@ class ShowBottomModalAction extends EnsembleAction {
96
36
initiator: initiator,
97
37
inputs: Utils .getMap (payload['inputs' ]),
98
38
body: body,
99
- styles: Utils .getMap (payload['styles' ]),
100
- options: Utils .getMap (payload['options' ]),
101
- onDismiss: EnsembleAction .fromYaml (payload['onDismiss' ]));
39
+ onDismiss: EnsembleAction .fromYaml (payload['onDismiss' ]),
40
+ payload: payload);
102
41
}
103
42
104
- @override
105
- Future <dynamic > execute (BuildContext context, ScopeManager scopeManager) {
106
- if (body == null ) return Future .value (null );
43
+ EdgeInsets ? margin (scopeManager) =>
44
+ Utils .optionalInsets (eval (payload["styles" ]? ["margin" ], scopeManager));
107
45
108
- // verticalSize: min | max
109
- // verticalAlignment: top | center | bottom
110
- // horizontalSize: min | max
111
- // horizontalAlignment: start | center | end
46
+ EdgeInsets ? padding (scopeManager) =>
47
+ Utils .optionalInsets (eval (payload["styles" ]? ["padding" ], scopeManager));
112
48
113
- // topRadius: 15
114
- // backgroundColor
115
- // barrierColor
49
+ EBorderRadius ? borderRadius (scopeManager) => Utils .getBorderRadius (
50
+ eval (payload["styles" ]? ["borderRadius" ], scopeManager));
116
51
117
- var topRadius =
118
- Radius .circular (getTopBorderRadius (scopeManager)? .toDouble () ?? 16 );
119
- var horizontalAlignment = getHorizontalAlignment (scopeManager);
120
- var widget = scopeManager.buildWidgetFromDefinition (body);
52
+ bool useSafeArea (scopeManager) =>
53
+ Utils .getBool (eval (payload["styles" ]? ["useSafeArea" ], scopeManager),
54
+ fallback: false );
55
+
56
+ Color ? getBarrierColor (scopeManager) =>
57
+ Utils .getColor (eval (payload["styles" ]? ['barrierColor' ], scopeManager));
121
58
122
- var bodyWidget = Material (
123
- type: MaterialType .transparency,
124
- elevation: 16 ,
125
- child: Container (
126
- decoration: BoxDecoration (
127
- color: getBackgroundColor (scopeManager) ??
128
- Theme .of (context).dialogBackgroundColor,
129
- borderRadius:
130
- BorderRadius .only (topLeft: topRadius, topRight: topRadius)),
131
- child: Column (
132
- // vertical
133
- mainAxisSize: getVerticalSize (scopeManager),
134
- mainAxisAlignment: getVerticalAlignment (scopeManager),
135
-
136
- // horizontal
137
- crossAxisAlignment: getHorizontalSize (scopeManager),
138
- children: [
139
- // account for the bottom notch
140
- SafeArea (
141
- bottom: false ,
142
- child: horizontalAlignment != null
143
- ? Align (alignment: horizontalAlignment, child: widget)
144
- : widget)
145
- ])),
146
- );
147
-
148
- showModalBottomSheet (
149
- context: context,
150
- // disable the default bottom sheet styling since we use our own
151
- backgroundColor: Colors .transparent,
152
- elevation: 16 ,
153
-
154
- barrierColor: getBarrierColor (scopeManager),
155
- isScrollControlled: true ,
156
- enableDrag: getEnableDrag (scopeManager),
157
- // padding to account for the keyboard when we have input widgets inside the modal
158
- builder: (modalContext) => Padding (
159
- padding: EdgeInsets .only (
160
- bottom: MediaQuery .of (modalContext).viewInsets.bottom,
161
- ),
162
- // have a bottom modal scope widget so we can close the modal
163
- child: BottomModalScopeWidget (
164
- rootContext: modalContext,
165
- // create a new Data Scope since the bottom modal is placed in a different context tree (directly under MaterialApp)
166
- child: DataScopeWidget (
167
- scopeManager: scopeManager.createChildScope (),
168
- child: bodyWidget),
169
- )),
170
- ).then ((payload) {
171
- if (onDismiss != null ) {
172
- return ScreenController ().executeActionWithScope (
173
- context, scopeManager, onDismiss! ,
174
- event: EnsembleEvent (null , data: payload));
175
- }
176
- });
59
+ Color ? getBackgroundColor (scopeManager) =>
60
+ Utils .getColor (eval (payload["styles" ]? ['backgroundColor' ], scopeManager));
61
+
62
+ bool ? isScrollable (scopeManager) =>
63
+ Utils .optionalBool (eval (payload["scrollable" ], scopeManager));
64
+
65
+ @override
66
+ Future <dynamic > execute (BuildContext context, ScopeManager scopeManager) {
67
+ if (body != null ) {
68
+ showModalBottomSheet (
69
+ context: context,
70
+ // disable the default bottom sheet styling since we use our own
71
+ backgroundColor: Colors .transparent,
72
+ elevation: 0 ,
73
+
74
+ barrierColor: getBarrierColor (scopeManager),
75
+ isScrollControlled: true ,
76
+ showDragHandle: true ,
77
+ enableDrag: true ,
78
+ // padding to account for the keyboard when we have input widgets inside the modal
79
+ builder: (modalContext) => Padding (
80
+ padding: EdgeInsets .only (
81
+ bottom: MediaQuery .of (modalContext).viewInsets.bottom,
82
+ ),
83
+ // have a bottom modal scope widget so we can close the modal
84
+ child: BottomModalScopeWidget (
85
+ rootContext: modalContext,
86
+ // create a new Data Scope since the bottom modal is placed in a different context tree (directly under MaterialApp)
87
+ child: DataScopeWidget (
88
+ scopeManager: scopeManager.createChildScope (),
89
+ child: getBodyWidget (scopeManager, context)),
90
+ )),
91
+ ).then ((payload) {
92
+ if (onDismiss != null ) {
93
+ return ScreenController ().executeActionWithScope (
94
+ context, scopeManager, onDismiss! ,
95
+ event: EnsembleEvent (null , data: payload));
96
+ }
97
+ });
98
+ }
177
99
return Future .value (null );
178
100
}
101
+
102
+ Widget getBodyWidget (ScopeManager scopeManager, BuildContext context) {
103
+ var widget = scopeManager.buildWidgetFromDefinition (body);
104
+ if (isScrollable (scopeManager) == true ) {
105
+ return DraggableScrollableSheet (
106
+ expand: false ,
107
+ builder: (context, scrollController) =>
108
+ buildRootContainer (scopeManager, context,
109
+ child: SingleChildScrollView (
110
+ controller: scrollController,
111
+ child: widget,
112
+ )));
113
+ }
114
+ return buildRootContainer (scopeManager, context, child: widget);
115
+ }
116
+
117
+ // This is the root container where all the root styling happen
118
+ Widget buildRootContainer (ScopeManager scopeManager, BuildContext context,
119
+ {required Widget child}) {
120
+ return Container (
121
+ margin: margin (scopeManager),
122
+ padding: padding (scopeManager),
123
+ decoration: BoxDecoration (
124
+ color: getBackgroundColor (scopeManager) ??
125
+ Theme .of (context).dialogBackgroundColor,
126
+ borderRadius: borderRadius (scopeManager)? .getValue () ??
127
+ const BorderRadius .only (
128
+ topLeft: defaultTopBorderRadius,
129
+ topRight: defaultTopBorderRadius)),
130
+ clipBehavior: Clip .antiAlias,
131
+ width: double .infinity, // stretch width 100%
132
+ child: useSafeArea (scopeManager) ? SafeArea (child: child) : child);
133
+ }
179
134
}
180
135
181
136
/// Dismiss the Bottom Modal (if the context is a descendant, no-op otherwise)
0 commit comments