@@ -286,7 +286,7 @@ class PageState extends State<Page>
286
286
dynamic appBar = _buildAppBar (pageModel.headerModel! ,
287
287
scrollableView: true ,
288
288
showNavigationIcon: pageModel.runtimeStyles? ['showNavigationIcon' ]);
289
- if (appBar is SliverAppBar ) {
289
+ if (appBar is SliverAppBar || appBar is AnimatedAppBar ) {
290
290
return appBar;
291
291
}
292
292
}
@@ -319,9 +319,12 @@ class PageState extends State<Page>
319
319
dynamic _buildAppBar (HeaderModel headerModel,
320
320
{required bool scrollableView, bool ? showNavigationIcon}) {
321
321
Widget ? titleWidget;
322
+
323
+
322
324
if (headerModel.titleWidget != null ) {
323
325
titleWidget = _scopeManager.buildWidget (headerModel.titleWidget! );
324
326
}
327
+
325
328
if (titleWidget == null && headerModel.titleText != null ) {
326
329
final title = _scopeManager.dataContext.eval (headerModel.titleText);
327
330
titleWidget = Text (Utils .translate (title.toString (), context));
@@ -351,15 +354,29 @@ class PageState extends State<Page>
351
354
Color ? shadowColor = Utils .getColor (evaluatedHeader? ['shadowColor' ]);
352
355
double ? elevation =
353
356
Utils .optionalInt (evaluatedHeader? ['elevation' ], min: 0 )? .toDouble ();
354
-
357
+ ScrollMode scrollMode =
358
+ Utils .getEnum <ScrollMode >(evaluatedHeader? ['scrollMode' ], ScrollMode .values);
355
359
final titleBarHeight =
356
360
Utils .optionalInt (evaluatedHeader? ['titleBarHeight' ], min: 0 )
357
361
? .toDouble () ??
358
362
kToolbarHeight;
359
363
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
+ }
360
376
// applicable only to Sliver scrolling
361
377
double ? flexibleMaxHeight =
362
378
Utils .optionalInt (evaluatedHeader? ['flexibleMaxHeight' ])? .toDouble ();
379
+
363
380
double ? flexibleMinHeight =
364
381
Utils .optionalInt (evaluatedHeader? ['flexibleMinHeight' ])? .toDouble ();
365
382
// collapsed height if specified needs to be bigger than titleBar height
@@ -368,28 +385,32 @@ class PageState extends State<Page>
368
385
}
369
386
370
387
if (scrollableView) {
371
- return SliverAppBar (
388
+ return AnimatedAppBar ( scrollController : externalScrollController ! ,
372
389
automaticallyImplyLeading:
373
- leadingWidget == null && showNavigationIcon != false ,
374
- leading : leadingWidget,
375
- title : titleWidget,
390
+ leadingWidget == null && showNavigationIcon != false ,
391
+ leadingWidget : leadingWidget,
392
+ titleWidget : titleWidget,
376
393
centerTitle: centerTitle,
377
394
backgroundColor: backgroundColor,
378
395
surfaceTintColor: surfaceTintColor,
379
396
foregroundColor: color,
397
+ animated: animationEnabled,
380
398
381
399
// control the drop shadow on the header's bottom edge
382
400
elevation: elevation,
383
401
shadowColor: shadowColor,
384
402
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,
386
411
387
- flexibleSpace: wrapsInFlexible (backgroundWidget),
388
- expandedHeight: flexibleMaxHeight,
389
- collapsedHeight: flexibleMinHeight,
390
-
391
- pinned: true ,
392
412
);
413
+
393
414
} else {
394
415
return AppBar (
395
416
automaticallyImplyLeading:
@@ -583,9 +604,10 @@ class PageState extends State<Page>
583
604
584
605
Widget buildScrollablePageContent (bool hasDrawer) {
585
606
List <Widget > slivers = [];
586
-
607
+ externalScrollController = ScrollController ();
587
608
// appBar
588
609
Widget ? appBar = buildSliverAppBar (widget._pageModel, hasDrawer);
610
+
589
611
if (appBar != null ) {
590
612
slivers.add (appBar);
591
613
}
@@ -840,6 +862,127 @@ class PageState extends State<Page>
840
862
}
841
863
}
842
864
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
+ }
843
986
class ActionResponse {
844
987
Map <String , dynamic >? _resultData;
845
988
Set <Function > listeners = {};
0 commit comments