Skip to content

Commit 2556461

Browse files
authored
Merge branch 'main' into unitTesting
2 parents 908e15d + 9c2e850 commit 2556461

File tree

17 files changed

+210
-60
lines changed

17 files changed

+210
-60
lines changed

modules/auth/pubspec.yaml

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ dependencies:
3939

4040
get_it: ^8.0.0
4141
google_sign_in: ^6.1.4
42-
sign_in_with_apple: ^5.0.0
42+
sign_in_with_apple: ^6.1.4
4343
firebase_auth: ^5.3.1
4444
firebase_core: ^3.6.0
45-
flutter_web_auth_2: ^2.1.4
45+
flutter_web_auth_2: ^4.1.0
4646
flutter_svg: ^2.0.7
4747
crypto: ^3.0.3
4848
# auth0_flutter: ^1.2.1

modules/ensemble/lib/action/saveFile/save_mobile.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import 'dart:io';
22

33
import 'package:flutter/foundation.dart';
4-
import 'package:image_gallery_saver/image_gallery_saver.dart';
54
import 'package:path_provider/path_provider.dart';
5+
import 'package:vision_gallery_saver/vision_gallery_saver.dart';
66
// Conditionally import the file that has `dart:html` vs. the stub:
77
import 'download_stub.dart' if (dart.library.html) 'download_web.dart';
88

@@ -11,7 +11,7 @@ Future<void> saveImageToDCIM(String fileName, Uint8List fileBytes) async {
1111
if (kIsWeb) {
1212
downloadFileOnWeb(fileName, fileBytes);
1313
} else {
14-
final result = await ImageGallerySaver.saveImage(
14+
final result = await VisionGallerySaver.saveImage(
1515
fileBytes,
1616
name: fileName,
1717
);

modules/ensemble/lib/framework/view/page.dart

+156-13
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ class PageState extends State<Page>
286286
dynamic appBar = _buildAppBar(pageModel.headerModel!,
287287
scrollableView: true,
288288
showNavigationIcon: pageModel.runtimeStyles?['showNavigationIcon']);
289-
if (appBar is SliverAppBar) {
289+
if (appBar is SliverAppBar || appBar is AnimatedAppBar) {
290290
return appBar;
291291
}
292292
}
@@ -319,9 +319,12 @@ class PageState extends State<Page>
319319
dynamic _buildAppBar(HeaderModel headerModel,
320320
{required bool scrollableView, bool? showNavigationIcon}) {
321321
Widget? titleWidget;
322+
323+
322324
if (headerModel.titleWidget != null) {
323325
titleWidget = _scopeManager.buildWidget(headerModel.titleWidget!);
324326
}
327+
325328
if (titleWidget == null && headerModel.titleText != null) {
326329
final title = _scopeManager.dataContext.eval(headerModel.titleText);
327330
titleWidget = Text(Utils.translate(title.toString(), context));
@@ -351,15 +354,29 @@ class PageState extends State<Page>
351354
Color? shadowColor = Utils.getColor(evaluatedHeader?['shadowColor']);
352355
double? elevation =
353356
Utils.optionalInt(evaluatedHeader?['elevation'], min: 0)?.toDouble();
354-
357+
ScrollMode scrollMode =
358+
Utils.getEnum<ScrollMode>(evaluatedHeader?['scrollMode'], ScrollMode.values);
355359
final titleBarHeight =
356360
Utils.optionalInt(evaluatedHeader?['titleBarHeight'], min: 0)
357361
?.toDouble() ??
358362
kToolbarHeight;
359363

364+
// animation
365+
final animation = evaluatedHeader?['animation'] != null
366+
? EnsembleThemeManager.yamlToDart(evaluatedHeader?['animation'])
367+
: null;
368+
bool animationEnabled = false;
369+
int? duration;
370+
Curve? curve;
371+
if (animation != null) {
372+
animationEnabled = Utils.getBool(animation!['enabled'], fallback: false);
373+
duration = Utils.getInt(animation!['duration'], fallback: 0);
374+
curve = Utils.getCurve(animation!['curve']);
375+
}
360376
// applicable only to Sliver scrolling
361377
double? flexibleMaxHeight =
362378
Utils.optionalInt(evaluatedHeader?['flexibleMaxHeight'])?.toDouble();
379+
363380
double? flexibleMinHeight =
364381
Utils.optionalInt(evaluatedHeader?['flexibleMinHeight'])?.toDouble();
365382
// collapsed height if specified needs to be bigger than titleBar height
@@ -368,28 +385,32 @@ class PageState extends State<Page>
368385
}
369386

370387
if (scrollableView) {
371-
return SliverAppBar(
388+
return AnimatedAppBar( scrollController: externalScrollController!,
372389
automaticallyImplyLeading:
373-
leadingWidget == null && showNavigationIcon != false,
374-
leading: leadingWidget,
375-
title: titleWidget,
390+
leadingWidget == null && showNavigationIcon != false,
391+
leadingWidget: leadingWidget,
392+
titleWidget: titleWidget,
376393
centerTitle: centerTitle,
377394
backgroundColor: backgroundColor,
378395
surfaceTintColor: surfaceTintColor,
379396
foregroundColor: color,
397+
animated: animationEnabled,
380398

381399
// control the drop shadow on the header's bottom edge
382400
elevation: elevation,
383401
shadowColor: shadowColor,
384402

385-
toolbarHeight: titleBarHeight,
403+
titleBarHeight: titleBarHeight,
404+
curve: curve,
405+
duration: duration,
406+
backgroundWidget: backgroundWidget,
407+
expandedBarHeight: flexibleMaxHeight,
408+
collapsedBarHeight: flexibleMinHeight,
409+
floating: scrollMode == ScrollMode.floating,
410+
pinned: scrollMode == ScrollMode.pinned,
386411

387-
flexibleSpace: wrapsInFlexible(backgroundWidget),
388-
expandedHeight: flexibleMaxHeight,
389-
collapsedHeight: flexibleMinHeight,
390-
391-
pinned: true,
392412
);
413+
393414
} else {
394415
return AppBar(
395416
automaticallyImplyLeading:
@@ -583,9 +604,10 @@ class PageState extends State<Page>
583604

584605
Widget buildScrollablePageContent(bool hasDrawer) {
585606
List<Widget> slivers = [];
586-
607+
externalScrollController = ScrollController();
587608
// appBar
588609
Widget? appBar = buildSliverAppBar(widget._pageModel, hasDrawer);
610+
589611
if (appBar != null) {
590612
slivers.add(appBar);
591613
}
@@ -840,6 +862,127 @@ class PageState extends State<Page>
840862
}
841863
}
842864

865+
class AnimatedAppBar extends StatefulWidget {
866+
final ScrollController scrollController;
867+
final collapsedBarHeight;
868+
final expandedBarHeight;
869+
final automaticallyImplyLeading;
870+
final leadingWidget;
871+
final titleWidget;
872+
final centerTitle;
873+
final backgroundColor;
874+
final surfaceTintColor;
875+
final foregroundColor;
876+
final elevation;
877+
final shadowColor;
878+
final titleBarHeight;
879+
final backgroundWidget;
880+
final floating;
881+
final pinned;
882+
final animated;
883+
final curve;
884+
final duration;
885+
AnimatedAppBar(
886+
{Key? key,
887+
this.automaticallyImplyLeading,
888+
this.leadingWidget,
889+
this.titleWidget,
890+
this.centerTitle,
891+
this.backgroundColor,
892+
this.surfaceTintColor,
893+
this.foregroundColor,
894+
this.elevation,
895+
this.shadowColor,
896+
this.titleBarHeight,
897+
this.backgroundWidget,
898+
this.animated,
899+
this.floating,
900+
this.pinned,
901+
this.collapsedBarHeight,
902+
this.expandedBarHeight,
903+
required this.scrollController,
904+
this.curve,
905+
this.duration})
906+
: super(key: key);
907+
908+
@override
909+
_AnimatedAppBarState createState() => _AnimatedAppBarState();
910+
}
911+
912+
class _AnimatedAppBarState extends State<AnimatedAppBar> {
913+
bool isCollapsed = false;
914+
915+
@override
916+
void initState() {
917+
super.initState();
918+
widget.scrollController.addListener(_updateCollapseState);
919+
}
920+
921+
void _updateCollapseState() {
922+
bool newState = widget.scrollController.hasClients &&
923+
widget.scrollController.offset >
924+
(widget.expandedBarHeight - widget.collapsedBarHeight);
925+
926+
if (newState != isCollapsed) {
927+
setState(() {
928+
isCollapsed = newState;
929+
});
930+
}
931+
}
932+
933+
@override
934+
void dispose() {
935+
widget.scrollController.removeListener(_updateCollapseState);
936+
super.dispose();
937+
}
938+
939+
/// wraps the background in a FlexibleSpaceBar for automatic stretching and parallax effect.
940+
Widget? wrapsInFlexible(Widget? backgroundWidget) {
941+
if (backgroundWidget != null) {
942+
return FlexibleSpaceBar(
943+
background: backgroundWidget,
944+
collapseMode: CollapseMode.parallax,
945+
);
946+
}
947+
return null;
948+
}
949+
950+
@override
951+
Widget build(BuildContext context) {
952+
return SliverAppBar(
953+
collapsedHeight: widget.collapsedBarHeight,
954+
expandedHeight: widget.expandedBarHeight,
955+
pinned: widget.pinned,
956+
centerTitle: widget.centerTitle,
957+
title: widget.animated
958+
? AnimatedContainer(
959+
curve: widget.curve ?? Curves.easeIn,
960+
duration: Duration(
961+
milliseconds: widget.duration ?? 300), // Animation duration
962+
transform: Matrix4.translationValues(
963+
0, // No horizontal movement
964+
isCollapsed ? 0 : -100, // Move from top to bottom
965+
0, // No depth movement
966+
),
967+
child: widget.titleWidget, // Your title widget
968+
) : widget.titleWidget,
969+
elevation: widget.elevation,
970+
backgroundColor: widget.backgroundColor,
971+
flexibleSpace: wrapsInFlexible(widget.backgroundWidget),
972+
automaticallyImplyLeading: widget.automaticallyImplyLeading,
973+
leading: widget.leadingWidget,
974+
surfaceTintColor: widget.surfaceTintColor,
975+
foregroundColor: widget.foregroundColor,
976+
shadowColor: widget.shadowColor,
977+
toolbarHeight: widget.titleBarHeight,
978+
);
979+
}
980+
}
981+
982+
enum ScrollMode {
983+
floating,
984+
pinned,
985+
}
843986
class ActionResponse {
844987
Map<String, dynamic>? _resultData;
845988
Set<Function> listeners = {};

modules/ensemble/lib/framework/widget/widget.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ abstract class EWidgetState<W extends HasController>
109109
rtn = AnimatedOpacity(
110110
// If visible, apply opacity if specified, else default to 1
111111
opacity: widgetController.visible != false
112-
? (Utils.getValidOpacity(widgetController.opacity ?? 1) ?? 1)
112+
? (Utils.optionalDouble(widgetController.opacity ?? 1, min: 0, max: 1.0) ?? 1)
113113
: 0,
114114
duration: widgetController.visibilityTransitionDuration!,
115115
child: rtn);
@@ -125,7 +125,7 @@ abstract class EWidgetState<W extends HasController>
125125
if (widgetController.visibilityTransitionDuration == null &&
126126
widgetController.opacity != null) {
127127
rtn = Opacity(
128-
opacity: Utils.getValidOpacity(widgetController.opacity!) ?? 1,
128+
opacity: Utils.optionalDouble(widgetController.opacity!, min: 0, max: 1.0) ?? 1.0,
129129
child: rtn,
130130
);
131131
}

modules/ensemble/lib/layout/form.dart

+2
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class EnsembleForm extends StatefulWidget
6868
_controller.labelStyle = Utils.getTextStyle(value),
6969
'enabled': (value) => _controller.enabled = Utils.optionalBool(value),
7070
'readOnly': (value) => _controller.readOnly = Utils.optionalBool(value),
71+
'dismissibleKeyboard': (value) => _controller.dismissibleKeyboard = Utils.getBool(value, fallback: _controller.dismissibleKeyboard),
7172
'width': (value) => _controller.width = Utils.optionalInt(value),
7273
'height': (value) => _controller.height = Utils.optionalInt(value),
7374
'gap': (value) => _controller.gap =
@@ -110,6 +111,7 @@ class FormController extends WidgetController {
110111
String? labelOverflow;
111112
bool? enabled;
112113
bool? readOnly;
114+
bool dismissibleKeyboard = true;
113115

114116
// labelMaxWidth applicable only to labelPosition=start
115117
int? labelMaxWidth;

modules/ensemble/lib/layout/tab/base_tab_bar.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,15 @@ abstract class BaseTabBarState extends EWidgetState<BaseTabBar>
108108

109109
Widget _buildTabWidget(ScopeManager? scopeManager, TabItem tabItem) {
110110
final tabWidget = tabItem.tabWidget;
111+
final label = scopeManager!.dataContext.eval(tabItem.label);
111112
if (scopeManager != null && tabWidget != null) {
112113
final customWidget = scopeManager.buildWidgetFromDefinition(tabWidget);
113114
return Tab(
114115
child: customWidget,
115116
);
116117
}
117118
return Tab(
118-
text: tabItem.label,
119+
text: label,
119120
icon:
120121
tabItem.icon != null ? ensemble.Icon.fromModel(tabItem.icon!) : null,
121122
);

modules/ensemble/lib/page_model.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ class SinglePageModel extends PageModel with HasStyles {
398398
titleText: titleText,
399399
titleWidget: titleWidget,
400400
flexibleBackground: background,
401-
leadingWidget: leadingWidget,
401+
leadingWidget: leadingWidget,
402402
inlineStyles: styles,
403403
classList: classList);
404404
}

modules/ensemble/lib/util/utils.dart

-7
Original file line numberDiff line numberDiff line change
@@ -694,13 +694,6 @@ class Utils {
694694
return textAlign;
695695
}
696696

697-
static double? getValidOpacity(double opacity) {
698-
if (opacity < 0 || opacity > 1) {
699-
return 1;
700-
} else {
701-
return opacity;
702-
}
703-
}
704697

705698
static Curve? getCurve(String? curveType) {
706699
Curve? curve;

modules/ensemble/lib/widget/button.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,9 @@ class ButtonState extends EWidgetState<Button> {
280280
// if focus in on a formfield (e.g. TextField), clicking on button will
281281
// not remove focus, so its value is never updated. Unfocus here before
282282
// executing button click ensure we get all the latest value of the form fields
283-
FocusManager.instance.primaryFocus?.unfocus();
283+
if (widget._controller.submitForm == false) {
284+
FocusManager.instance.primaryFocus?.unfocus();
285+
}
284286

285287
// submit the form if specified
286288
if (widget._controller.submitForm == true) {

modules/ensemble/lib/widget/helpers/controllers.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ abstract class WidgetController extends Controller with HasStyles {
261261
'flex': (value) => flex = Utils.optionalInt(value, min: 1),
262262
'expanded': (value) => expanded = Utils.getBool(value, fallback: false),
263263
'visible': (value) => visible = Utils.getBool(value, fallback: true),
264-
'opacity': (value) => opacity = Utils.getValidOpacity(value),
264+
'opacity': (value) => opacity = Utils.optionalDouble(value, min: 0, max: 1),
265265
'visibilityTransitionDuration': (value) =>
266266
visibilityTransitionDuration = Utils.getDuration(value),
267267
'elevation': (value) =>
@@ -487,7 +487,7 @@ abstract class EnsembleWidgetController extends EnsembleController
487487
'flexMode': (value) => flexMode = FlexMode.values.from(value),
488488
'flex': (value) => flex = Utils.optionalInt(value, min: 1),
489489
'visible': (value) => visible = Utils.getBool(value, fallback: true),
490-
'opacity': (value) => opacity = Utils.getValidOpacity(value),
490+
'opacity': (value) => opacity = Utils.optionalDouble(value, min: 0, max: 1),
491491
'visibilityTransitionDuration': (value) =>
492492
visibilityTransitionDuration = Utils.getDuration(value),
493493
'textDirection': (value) => textDirection = Utils.getTextDirection(value),

modules/ensemble/lib/widget/helpers/form_helper.dart

+4
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ class FormHelper {
118118
event: EnsembleEvent(formState.widget));
119119
}
120120
}
121+
// only dismiss keyboard if dismissKeyboardOnSubmit is true (By default it is true)
122+
if(formState?.widget.controller.dismissibleKeyboard == true) {
123+
FocusManager.instance.primaryFocus?.unfocus();
124+
}
121125
}
122126
}
123127

0 commit comments

Comments
 (0)