From 6baa350eefcb11407b20b574d290e94e067f12ff Mon Sep 17 00:00:00 2001 From: Umair Manzoor Date: Fri, 14 Feb 2025 17:31:14 +0500 Subject: [PATCH 01/11] added animated Header --- modules/ensemble/lib/framework/view/page.dart | 125 ++++++++++++++++-- modules/ensemble/lib/page_model.dart | 20 ++- 2 files changed, 130 insertions(+), 15 deletions(-) diff --git a/modules/ensemble/lib/framework/view/page.dart b/modules/ensemble/lib/framework/view/page.dart index 6d906d958..d95c034fe 100644 --- a/modules/ensemble/lib/framework/view/page.dart +++ b/modules/ensemble/lib/framework/view/page.dart @@ -22,6 +22,7 @@ import 'package:ensemble/util/utils.dart'; import 'package:ensemble/widget/helpers/controllers.dart'; import 'package:ensemble/widget/helpers/unfocus.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; class SinglePageController extends WidgetController { TextStyleComposite? _textStyle; @@ -282,12 +283,14 @@ class PageState extends State /// create AppBar that is part of a CustomScrollView Widget? buildSliverAppBar(SinglePageModel pageModel, bool hasDrawer) { if (pageModel.headerModel != null) { - dynamic appBar = _buildAppBar(pageModel.headerModel!, - scrollableView: true, - showNavigationIcon: pageModel.runtimeStyles?['showNavigationIcon']); - if (appBar is SliverAppBar) { - return appBar; - } + dynamic appBar = _buildAppBar( + pageModel.headerModel!, + scrollableView: true, + showNavigationIcon: pageModel.runtimeStyles?['showNavigationIcon'], + ); + // if (appBar is SliverAppBar) { + return appBar; + //r } } if (hasDrawer) { return const SliverAppBar(); @@ -295,6 +298,21 @@ class PageState extends State return null; } + // build a subtitle Widget right below the AppBar + Widget? buildSubtitleWidget(SinglePageModel pageModel) { + if (pageModel.headerModel != null && + pageModel.headerModel!.subTitleWidget != null) { + dynamic appBar = _buildAppBar(pageModel.headerModel!, + scrollableView: true, + showNavigationIcon: pageModel.runtimeStyles?['showNavigationIcon'], + isSubtitle: true); + + return appBar; + } + + return null; + } + /// fixed AppBar PreferredSizeWidget? buildFixedAppBar( SinglePageModel pageModel, bool hasDrawer) { @@ -316,11 +334,19 @@ class PageState extends State /// fixed AppBar dynamic _buildAppBar(HeaderModel headerModel, - {required bool scrollableView, bool? showNavigationIcon}) { + {required bool scrollableView, + bool? showNavigationIcon, + bool? isSubtitle}) { Widget? titleWidget; + Widget? subTitleWidget; + if (headerModel.titleWidget != null) { titleWidget = _scopeManager.buildWidget(headerModel.titleWidget!); } + if (headerModel.subTitleWidget != null) { + subTitleWidget = _scopeManager.buildWidget(headerModel.subTitleWidget!); + } + if (titleWidget == null && headerModel.titleText != null) { final title = _scopeManager.dataContext.eval(headerModel.titleText); titleWidget = Text(Utils.translate(title.toString(), context)); @@ -344,6 +370,12 @@ class PageState extends State Color? shadowColor = Utils.getColor(evaluatedHeader?['shadowColor']); double? elevation = Utils.optionalInt(evaluatedHeader?['elevation'], min: 0)?.toDouble(); + bool? pinned = Utils.getBool(evaluatedHeader?['pinned'], fallback: false); + bool? snapped = Utils.getBool(evaluatedHeader?['snapped'], fallback: false); + bool? floating = + Utils.getBool(evaluatedHeader?['floating'], fallback: false); + bool? animation = + Utils.getBool(evaluatedHeader?['animation'], fallback: false); final titleBarHeight = Utils.optionalInt(evaluatedHeader?['titleBarHeight'], min: 0) @@ -353,6 +385,10 @@ class PageState extends State // applicable only to Sliver scrolling double? flexibleMaxHeight = Utils.optionalInt(evaluatedHeader?['flexibleMaxHeight'])?.toDouble(); + double? flexibleSubtitleHeight = + Utils.optionalInt(evaluatedHeader?['subtitleHeightOffSet']) + ?.toDouble(); + double? flexibleMinHeight = Utils.optionalInt(evaluatedHeader?['flexibleMinHeight'])?.toDouble(); // collapsed height if specified needs to be bigger than titleBar height @@ -361,6 +397,18 @@ class PageState extends State } if (scrollableView) { + if (subTitleWidget != null && isSubtitle == true) { + if(animation == true){ + return StickySliver( + height: flexibleSubtitleHeight!, + child: subTitleWidget, + ); + }else{ + return subTitleWidget; + } + + + } return SliverAppBar( automaticallyImplyLeading: showNavigationIcon != false, title: titleWidget, @@ -378,8 +426,11 @@ class PageState extends State flexibleSpace: wrapsInFlexible(backgroundWidget), expandedHeight: flexibleMaxHeight, collapsedHeight: flexibleMinHeight, + floating: floating ?? false, + pinned: pinned ?? false, - pinned: true, + // Only enable snap if floating is true + snap: (floating ?? false) ? (snapped ?? false) : false, ); } else { return AppBar( @@ -575,10 +626,15 @@ class PageState extends State // appBar Widget? appBar = buildSliverAppBar(widget._pageModel, hasDrawer); + if (appBar != null) { slivers.add(appBar); } - + // + Widget? subtitle = buildSubtitleWidget(widget._pageModel); + if (subtitle != null) { + slivers.add(subtitle); + } // body slivers.add(SliverToBoxAdapter( child: getBody(appBar != null), @@ -782,6 +838,57 @@ class PageState extends State } } +class RenderStickySliver extends RenderSliverSingleBoxAdapter { + RenderStickySliver({RenderBox? child, required this.height}) + : super(child: child); + final double height; + @override + void performLayout() { + final double scrollOffset = constraints.scrollOffset; + final double maxExtent = height; + bool repaint = false; + + // Progress of the scroll (from 0 to 1) + double progress = (scrollOffset / maxExtent).clamp(0.0, 1.0); + + if (child != null) { + // Adjust the height of the child based on the scroll progress + final double childHeight = + 100 * progress; // 100 is the full height of the sticky sliver + child!.layout( + constraints.asBoxConstraints(maxExtent: childHeight), + parentUsesSize: true, + ); + + + // Set the geometry for the sliver + geometry = SliverGeometry( + scrollExtent: childHeight, + paintExtent: childHeight, + maxPaintExtent: childHeight, + paintOrigin: scrollOffset + kToolbarHeight, + ); + setChildParentData(child!, constraints, geometry!); + } + } +} + +class StickySliver extends SingleChildRenderObjectWidget { + StickySliver({Widget? child, required this.height, Key? key}) + : super(child: child, key: key); + final double height; + @override + RenderObject createRenderObject(BuildContext context) { + return RenderStickySliver(height: height); + } + + @override + void updateRenderObject( + BuildContext context, RenderStickySliver renderObject) { + // No update needed + } +} + class ActionResponse { Map? _resultData; Set listeners = {}; diff --git a/modules/ensemble/lib/page_model.dart b/modules/ensemble/lib/page_model.dart index ecc3eae24..08addeaf7 100644 --- a/modules/ensemble/lib/page_model.dart +++ b/modules/ensemble/lib/page_model.dart @@ -351,6 +351,7 @@ class SinglePageModel extends PageModel with HasStyles { void processHeader(YamlMap? headerData, String? legacyTitle) { WidgetModel? titleWidget; + WidgetModel? subTitleWidget; String? titleText = legacyTitle; WidgetModel? background; Map? styles; @@ -376,7 +377,10 @@ class SinglePageModel extends PageModel with HasStyles { background = ViewUtil.buildModel( headerData['flexibleBackground'], customViewDefinitions); } - + if (headerData['subtitleWidget'] != null) { + subTitleWidget = ViewUtil.buildModel( + headerData['subtitleWidget'], customViewDefinitions); + } styles = EnsembleThemeManager.yamlToDart(headerData['styles']); classList = HasStyles.toClassList( headerData[ViewUtil.classNameAttribute] as String?); @@ -388,11 +392,13 @@ class SinglePageModel extends PageModel with HasStyles { styles != null || classList != null) { headerModel = HeaderModel( - titleText: titleText, - titleWidget: titleWidget, - flexibleBackground: background, - inlineStyles: styles, - classList: classList); + titleText: titleText, + titleWidget: titleWidget, + subTitleWidget: subTitleWidget, + flexibleBackground: background, + inlineStyles: styles, + classList: classList, + ); } } @@ -532,6 +538,7 @@ class HeaderModel extends Object with HasStyles { HeaderModel( {this.titleText, this.titleWidget, + this.subTitleWidget, this.flexibleBackground, inlineStyles, classList}) { @@ -542,6 +549,7 @@ class HeaderModel extends Object with HasStyles { // header title can be text or a widget String? titleText; WidgetModel? titleWidget; + WidgetModel? subTitleWidget; WidgetModel? flexibleBackground; } From 13c9a092125088284e5afd15ba4bdd02a7da6d1f Mon Sep 17 00:00:00 2001 From: Umair Manzoor Date: Tue, 18 Feb 2025 00:56:27 +0500 Subject: [PATCH 02/11] made requested changes for animated Header --- modules/ensemble/lib/framework/view/page.dart | 11 +++++------ modules/ensemble/lib/page_model.dart | 1 - 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/modules/ensemble/lib/framework/view/page.dart b/modules/ensemble/lib/framework/view/page.dart index ec50e2798..90d10788a 100644 --- a/modules/ensemble/lib/framework/view/page.dart +++ b/modules/ensemble/lib/framework/view/page.dart @@ -289,9 +289,9 @@ class PageState extends State scrollableView: true, showNavigationIcon: pageModel.runtimeStyles?['showNavigationIcon'], ); - // if (appBar is SliverAppBar) { + if (appBar is SliverAppBar) { return appBar; - //r } + } } if (hasDrawer) { return const SliverAppBar(); @@ -435,11 +435,11 @@ class PageState extends State flexibleSpace: wrapsInFlexible(backgroundWidget), expandedHeight: flexibleMaxHeight, collapsedHeight: flexibleMinHeight, - floating: floating ?? false, - pinned: pinned ?? false, + floating: floating, + pinned: pinned, // Only enable snap if floating is true - snap: (floating ?? false) ? (snapped ?? false) : false, + snap: floating ? snapped : false, ); } else { return AppBar( @@ -904,7 +904,6 @@ class RenderStickySliver extends RenderSliverSingleBoxAdapter { void performLayout() { final double scrollOffset = constraints.scrollOffset; final double maxExtent = height; - bool repaint = false; // Progress of the scroll (from 0 to 1) double progress = (scrollOffset / maxExtent).clamp(0.0, 1.0); diff --git a/modules/ensemble/lib/page_model.dart b/modules/ensemble/lib/page_model.dart index 66a91cda9..b513dc26d 100644 --- a/modules/ensemble/lib/page_model.dart +++ b/modules/ensemble/lib/page_model.dart @@ -565,7 +565,6 @@ class HeaderModel extends Object with HasStyles { WidgetModel? flexibleBackground; } - class FooterItems extends Object with HasStyles { final List children; Map? inlineStyles; From d3cdbbf816edb0c23cc359b6b275b7a4175ebdb7 Mon Sep 17 00:00:00 2001 From: Umair Manzoor Date: Mon, 24 Feb 2025 21:02:16 +0500 Subject: [PATCH 03/11] enum for floating and pinned AppBar, Animation Map for sliver AppBar --- modules/ensemble/lib/framework/view/page.dart | 231 +++++++++++------- modules/ensemble/lib/page_model.dart | 9 - 2 files changed, 146 insertions(+), 94 deletions(-) diff --git a/modules/ensemble/lib/framework/view/page.dart b/modules/ensemble/lib/framework/view/page.dart index 90d10788a..d7cc67f12 100644 --- a/modules/ensemble/lib/framework/view/page.dart +++ b/modules/ensemble/lib/framework/view/page.dart @@ -23,7 +23,7 @@ import 'package:ensemble/util/utils.dart'; import 'package:ensemble/widget/helpers/controllers.dart'; import 'package:ensemble/widget/helpers/unfocus.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; + class SinglePageController extends WidgetController { TextStyleComposite? _textStyle; @@ -289,9 +289,9 @@ class PageState extends State scrollableView: true, showNavigationIcon: pageModel.runtimeStyles?['showNavigationIcon'], ); - if (appBar is SliverAppBar) { + // if (appBar is SliverAppBar) { return appBar; - } + // }1 } if (hasDrawer) { return const SliverAppBar(); @@ -299,21 +299,6 @@ class PageState extends State return null; } - // build a subtitle Widget right below the AppBar - Widget? buildSubtitleWidget(SinglePageModel pageModel) { - if (pageModel.headerModel != null && - pageModel.headerModel!.subTitleWidget != null) { - dynamic appBar = _buildAppBar(pageModel.headerModel!, - scrollableView: true, - showNavigationIcon: pageModel.runtimeStyles?['showNavigationIcon'], - isSubtitle: true); - - return appBar; - } - - return null; - } - /// fixed AppBar PreferredSizeWidget? buildFixedAppBar( SinglePageModel pageModel, bool hasDrawer) { @@ -337,16 +322,13 @@ class PageState extends State dynamic _buildAppBar(HeaderModel headerModel, {required bool scrollableView, bool? showNavigationIcon, - bool? isSubtitle}) { + }) { Widget? titleWidget; - Widget? subTitleWidget; + if (headerModel.titleWidget != null) { titleWidget = _scopeManager.buildWidget(headerModel.titleWidget!); } - if (headerModel.subTitleWidget != null) { - subTitleWidget = _scopeManager.buildWidget(headerModel.subTitleWidget!); - } if (titleWidget == null && headerModel.titleText != null) { final title = _scopeManager.dataContext.eval(headerModel.titleText); @@ -381,20 +363,21 @@ class PageState extends State bool? snapped = Utils.getBool(evaluatedHeader?['snapped'], fallback: false); bool? floating = Utils.getBool(evaluatedHeader?['floating'], fallback: false); - bool? animation = - Utils.getBool(evaluatedHeader?['animation'], fallback: false); - + appBarBehaviour behaviour = + Utils.getEnum(evaluatedHeader?['scrollMode'], appBarBehaviour.values); final titleBarHeight = Utils.optionalInt(evaluatedHeader?['titleBarHeight'], min: 0) ?.toDouble() ?? kToolbarHeight; + // animation + final animation = EnsembleThemeManager.yamlToDart(evaluatedHeader?['animation']); + final animationEnabled = Utils.getBool(animation!['enabled'], fallback: false); + final duration = Utils.getInt(animation!['duration'], fallback: 0); + final curve = Utils.getCurve(animation!['curve']); // applicable only to Sliver scrolling double? flexibleMaxHeight = Utils.optionalInt(evaluatedHeader?['flexibleMaxHeight'])?.toDouble(); - double? flexibleSubtitleHeight = - Utils.optionalInt(evaluatedHeader?['subtitleHeightOffSet']) - ?.toDouble(); double? flexibleMinHeight = Utils.optionalInt(evaluatedHeader?['flexibleMinHeight'])?.toDouble(); @@ -404,16 +387,33 @@ class PageState extends State } if (scrollableView) { - if (subTitleWidget != null && isSubtitle == true) { - if(animation == true){ - return StickySliver( - height: flexibleSubtitleHeight!, - child: subTitleWidget, + if(animationEnabled== true){ + return AnimatedAppBar( scrollController: externalScrollController!, + automaticallyImplyLeading: + leadingWidget == null && showNavigationIcon != false, + leadingWidget: leadingWidget, + titleWidget: titleWidget, + centerTitle: centerTitle, + backgroundColor: backgroundColor, + surfaceTintColor: surfaceTintColor, + foregroundColor: color, + + // control the drop shadow on the header's bottom edge + elevation: elevation, + shadowColor: shadowColor, + + titleBarHeight: titleBarHeight, + curve: curve, + duration: duration, + backgroundWidget: backgroundWidget, + expandedBarHeight: flexibleMaxHeight, + collapsedBarHeight: flexibleMinHeight, + floating: behaviour == appBarBehaviour.floating, + pinned: behaviour == appBarBehaviour.pinned, + + // Only enable snap if floating is true + snap: floating ? snapped : false, ); - }else{ - return subTitleWidget; - } - } return SliverAppBar( @@ -435,8 +435,8 @@ class PageState extends State flexibleSpace: wrapsInFlexible(backgroundWidget), expandedHeight: flexibleMaxHeight, collapsedHeight: flexibleMinHeight, - floating: floating, - pinned: pinned, + floating: behaviour == appBarBehaviour.floating, + pinned: behaviour == appBarBehaviour.pinned, // Only enable snap if floating is true snap: floating ? snapped : false, @@ -634,18 +634,13 @@ class PageState extends State Widget buildScrollablePageContent(bool hasDrawer) { List slivers = []; - + externalScrollController = ScrollController(); // appBar Widget? appBar = buildSliverAppBar(widget._pageModel, hasDrawer); if (appBar != null) { slivers.add(appBar); } - // - Widget? subtitle = buildSubtitleWidget(widget._pageModel); - if (subtitle != null) { - slivers.add(subtitle); - } // body slivers.add(SliverToBoxAdapter( child: getBody(appBar != null), @@ -896,56 +891,122 @@ class PageState extends State } } -class RenderStickySliver extends RenderSliverSingleBoxAdapter { - RenderStickySliver({RenderBox? child, required this.height}) - : super(child: child); - final double height; +class AnimatedAppBar extends StatefulWidget { + final ScrollController scrollController; + final collapsedBarHeight; + final expandedBarHeight; + final automaticallyImplyLeading; + final leadingWidget; + final titleWidget; + final centerTitle; + final backgroundColor; + final surfaceTintColor; + final foregroundColor; + final elevation; + final shadowColor; + final titleBarHeight; + final backgroundWidget; + final floating; + final pinned; + final snap; + final curve; + final duration; + AnimatedAppBar({Key? key, + this.automaticallyImplyLeading, + this.leadingWidget, + this.titleWidget, + this.centerTitle, + this.backgroundColor, + this.surfaceTintColor, + this.foregroundColor, + this.elevation, + this.shadowColor, + this.titleBarHeight, + this.backgroundWidget, + this.floating, + this.pinned, + this.snap, + this.collapsedBarHeight, + this.expandedBarHeight, required this.scrollController, + this.curve, + this.duration}) : super(key: key); + @override - void performLayout() { - final double scrollOffset = constraints.scrollOffset; - final double maxExtent = height; - - // Progress of the scroll (from 0 to 1) - double progress = (scrollOffset / maxExtent).clamp(0.0, 1.0); - - if (child != null) { - // Adjust the height of the child based on the scroll progress - final double childHeight = - 100 * progress; // 100 is the full height of the sticky sliver - child!.layout( - constraints.asBoxConstraints(maxExtent: childHeight), - parentUsesSize: true, - ); + _AnimatedAppBarState createState() => _AnimatedAppBarState(); +} +class _AnimatedAppBarState extends State { - // Set the geometry for the sliver - geometry = SliverGeometry( - scrollExtent: childHeight, - paintExtent: childHeight, - maxPaintExtent: childHeight, - paintOrigin: scrollOffset + kToolbarHeight, - ); - setChildParentData(child!, constraints, geometry!); + bool isCollapsed = false; + + @override + void initState() { + super.initState(); + widget.scrollController.addListener(_updateCollapseState); + } + + void _updateCollapseState() { + bool newState = widget.scrollController.hasClients && + widget.scrollController.offset > (widget.expandedBarHeight - widget.collapsedBarHeight); + + if (newState != isCollapsed) { + setState(() { + isCollapsed = newState; + }); } } -} -class StickySliver extends SingleChildRenderObjectWidget { - StickySliver({Widget? child, required this.height, Key? key}) - : super(child: child, key: key); - final double height; @override - RenderObject createRenderObject(BuildContext context) { - return RenderStickySliver(height: height); + void dispose() { + widget.scrollController.removeListener(_updateCollapseState); + super.dispose(); + } + /// wraps the background in a FlexibleSpaceBar for automatic stretching and parallax effect. + Widget? wrapsInFlexible(Widget? backgroundWidget) { + if (backgroundWidget != null) { + return FlexibleSpaceBar( + background: backgroundWidget, + collapseMode: CollapseMode.parallax, + ); + } + return null; } - @override - void updateRenderObject( - BuildContext context, RenderStickySliver renderObject) { - // No update needed + Widget build(BuildContext context) { + return SliverAppBar( + collapsedHeight: widget.collapsedBarHeight, + expandedHeight: widget.expandedBarHeight, + pinned: widget.pinned, + centerTitle: widget.centerTitle, + title: AnimatedContainer( + curve: widget.curve, + duration: Duration(milliseconds: widget.duration), // Animation duration + transform: Matrix4.translationValues( + 0, // No horizontal movement + isCollapsed ? 0 : -100, // Move from top to bottom + 0, // No depth movement + ), + child: widget.titleWidget, // Your title widget + ), + elevation: widget.elevation, + backgroundColor: widget.backgroundColor, + flexibleSpace: wrapsInFlexible(widget.backgroundWidget), + automaticallyImplyLeading: widget.automaticallyImplyLeading, + leading: widget.leadingWidget, + surfaceTintColor: widget.surfaceTintColor, + foregroundColor: widget.foregroundColor, + shadowColor: widget.shadowColor, + toolbarHeight: widget.titleBarHeight, + // Only enable snap if floating is true + snap: widget.snap, + ); } } - +enum appBarBehaviour { + floating, + snap, + pinned, +} class ActionResponse { Map? _resultData; Set listeners = {}; diff --git a/modules/ensemble/lib/page_model.dart b/modules/ensemble/lib/page_model.dart index b513dc26d..e910ec03e 100644 --- a/modules/ensemble/lib/page_model.dart +++ b/modules/ensemble/lib/page_model.dart @@ -351,7 +351,6 @@ class SinglePageModel extends PageModel with HasStyles { void processHeader(YamlMap? headerData, String? legacyTitle) { WidgetModel? titleWidget; - WidgetModel? subTitleWidget; String? titleText = legacyTitle; WidgetModel? background; WidgetModel? leadingWidget; @@ -383,10 +382,6 @@ class SinglePageModel extends PageModel with HasStyles { background = ViewUtil.buildModel( headerData['flexibleBackground'], customViewDefinitions); } - if (headerData['subtitleWidget'] != null) { - subTitleWidget = ViewUtil.buildModel( - headerData['subtitleWidget'], customViewDefinitions); - } styles = EnsembleThemeManager.yamlToDart(headerData['styles']); classList = HasStyles.toClassList( headerData[ViewUtil.classNameAttribute] as String?); @@ -402,7 +397,6 @@ class SinglePageModel extends PageModel with HasStyles { titleText: titleText, titleWidget: titleWidget, - subTitleWidget: subTitleWidget, flexibleBackground: background, leadingWidget: leadingWidget, inlineStyles: styles, @@ -547,7 +541,6 @@ class HeaderModel extends Object with HasStyles { HeaderModel( {this.titleText, this.titleWidget, - this.subTitleWidget, this.flexibleBackground, this.leadingWidget, inlineStyles, @@ -559,10 +552,8 @@ class HeaderModel extends Object with HasStyles { // header title can be text or a widget String? titleText; WidgetModel? titleWidget; - WidgetModel? subTitleWidget; WidgetModel? leadingWidget; - WidgetModel? flexibleBackground; } class FooterItems extends Object with HasStyles { From 29e2310299a74c5fe4f2be8afae9d24fce7d86de Mon Sep 17 00:00:00 2001 From: Umair Manzoor Date: Mon, 24 Feb 2025 21:22:25 +0500 Subject: [PATCH 04/11] additional check --- modules/ensemble/lib/framework/view/page.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ensemble/lib/framework/view/page.dart b/modules/ensemble/lib/framework/view/page.dart index d7cc67f12..ff4ab513d 100644 --- a/modules/ensemble/lib/framework/view/page.dart +++ b/modules/ensemble/lib/framework/view/page.dart @@ -289,9 +289,9 @@ class PageState extends State scrollableView: true, showNavigationIcon: pageModel.runtimeStyles?['showNavigationIcon'], ); - // if (appBar is SliverAppBar) { + if (appBar is SliverAppBar || appBar is AnimatedAppBar) { return appBar; - // }1 + } } if (hasDrawer) { return const SliverAppBar(); From 068d87011ddfb8452bd18ff3f99fb1c8d0e3a697 Mon Sep 17 00:00:00 2001 From: Umair Manzoor Date: Mon, 24 Feb 2025 21:45:22 +0500 Subject: [PATCH 05/11] code cleansing --- modules/ensemble/lib/framework/view/page.dart | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/modules/ensemble/lib/framework/view/page.dart b/modules/ensemble/lib/framework/view/page.dart index ff4ab513d..dad29a12a 100644 --- a/modules/ensemble/lib/framework/view/page.dart +++ b/modules/ensemble/lib/framework/view/page.dart @@ -359,12 +359,8 @@ class PageState extends State Color? shadowColor = Utils.getColor(evaluatedHeader?['shadowColor']); double? elevation = Utils.optionalInt(evaluatedHeader?['elevation'], min: 0)?.toDouble(); - bool? pinned = Utils.getBool(evaluatedHeader?['pinned'], fallback: false); - bool? snapped = Utils.getBool(evaluatedHeader?['snapped'], fallback: false); - bool? floating = - Utils.getBool(evaluatedHeader?['floating'], fallback: false); - appBarBehaviour behaviour = - Utils.getEnum(evaluatedHeader?['scrollMode'], appBarBehaviour.values); + AppBarBehavior behaviour = + Utils.getEnum(evaluatedHeader?['scrollMode'], AppBarBehavior.values); final titleBarHeight = Utils.optionalInt(evaluatedHeader?['titleBarHeight'], min: 0) ?.toDouble() ?? @@ -408,11 +404,9 @@ class PageState extends State backgroundWidget: backgroundWidget, expandedBarHeight: flexibleMaxHeight, collapsedBarHeight: flexibleMinHeight, - floating: behaviour == appBarBehaviour.floating, - pinned: behaviour == appBarBehaviour.pinned, + floating: behaviour == AppBarBehavior.floating, + pinned: behaviour == AppBarBehavior.pinned, - // Only enable snap if floating is true - snap: floating ? snapped : false, ); } @@ -435,11 +429,9 @@ class PageState extends State flexibleSpace: wrapsInFlexible(backgroundWidget), expandedHeight: flexibleMaxHeight, collapsedHeight: flexibleMinHeight, - floating: behaviour == appBarBehaviour.floating, - pinned: behaviour == appBarBehaviour.pinned, + floating: behaviour == AppBarBehavior.floating, + pinned: behaviour == AppBarBehavior.pinned, - // Only enable snap if floating is true - snap: floating ? snapped : false, ); } else { return AppBar( @@ -1002,7 +994,7 @@ class _AnimatedAppBarState extends State { ); } } -enum appBarBehaviour { +enum AppBarBehavior { floating, snap, pinned, From 86f2472e90a8e4210db43a63840851b68d11d116 Mon Sep 17 00:00:00 2001 From: Umair Manzoor Date: Tue, 25 Feb 2025 02:58:57 +0500 Subject: [PATCH 06/11] Slivebar moved to new bar class --- modules/ensemble/lib/framework/view/page.dart | 56 ++++++------------- 1 file changed, 17 insertions(+), 39 deletions(-) diff --git a/modules/ensemble/lib/framework/view/page.dart b/modules/ensemble/lib/framework/view/page.dart index dad29a12a..4cb6eebee 100644 --- a/modules/ensemble/lib/framework/view/page.dart +++ b/modules/ensemble/lib/framework/view/page.dart @@ -367,10 +367,15 @@ class PageState extends State kToolbarHeight; // animation - final animation = EnsembleThemeManager.yamlToDart(evaluatedHeader?['animation']); - final animationEnabled = Utils.getBool(animation!['enabled'], fallback: false); - final duration = Utils.getInt(animation!['duration'], fallback: 0); - final curve = Utils.getCurve(animation!['curve']); + final animation = evaluatedHeader?['animation']!= null? EnsembleThemeManager.yamlToDart(evaluatedHeader?['animation']):null; + bool animationEnabled = false; + int? duration; + Curve? curve; + if(animation != null){ + animationEnabled = Utils.getBool(animation!['enabled'], fallback: false); + duration = Utils.getInt(animation!['duration'], fallback: 0); + curve = Utils.getCurve(animation!['curve']); + } // applicable only to Sliver scrolling double? flexibleMaxHeight = Utils.optionalInt(evaluatedHeader?['flexibleMaxHeight'])?.toDouble(); @@ -383,7 +388,6 @@ class PageState extends State } if (scrollableView) { - if(animationEnabled== true){ return AnimatedAppBar( scrollController: externalScrollController!, automaticallyImplyLeading: leadingWidget == null && showNavigationIcon != false, @@ -393,7 +397,7 @@ class PageState extends State backgroundColor: backgroundColor, surfaceTintColor: surfaceTintColor, foregroundColor: color, - + animated: animationEnabled, // control the drop shadow on the header's bottom edge elevation: elevation, shadowColor: shadowColor, @@ -409,30 +413,6 @@ class PageState extends State ); - } - return SliverAppBar( - automaticallyImplyLeading: - leadingWidget == null && showNavigationIcon != false, - leading: leadingWidget, - title: titleWidget, - centerTitle: centerTitle, - backgroundColor: backgroundColor, - surfaceTintColor: surfaceTintColor, - foregroundColor: color, - - // control the drop shadow on the header's bottom edge - elevation: elevation, - shadowColor: shadowColor, - - toolbarHeight: titleBarHeight, - - flexibleSpace: wrapsInFlexible(backgroundWidget), - expandedHeight: flexibleMaxHeight, - collapsedHeight: flexibleMinHeight, - floating: behaviour == AppBarBehavior.floating, - pinned: behaviour == AppBarBehavior.pinned, - - ); } else { return AppBar( automaticallyImplyLeading: @@ -900,7 +880,7 @@ class AnimatedAppBar extends StatefulWidget { final backgroundWidget; final floating; final pinned; - final snap; + final animated; final curve; final duration; AnimatedAppBar({Key? key, @@ -915,9 +895,10 @@ class AnimatedAppBar extends StatefulWidget { this.shadowColor, this.titleBarHeight, this.backgroundWidget, + this.animated, this.floating, this.pinned, - this.snap, + this.collapsedBarHeight, this.expandedBarHeight, required this.scrollController, this.curve, @@ -970,16 +951,16 @@ class _AnimatedAppBarState extends State { expandedHeight: widget.expandedBarHeight, pinned: widget.pinned, centerTitle: widget.centerTitle, - title: AnimatedContainer( - curve: widget.curve, - duration: Duration(milliseconds: widget.duration), // Animation duration + title: widget.animated ? AnimatedContainer( + curve: widget.curve ?? Curves.easeIn, + duration: Duration(milliseconds: widget.duration ?? 300), // Animation duration transform: Matrix4.translationValues( 0, // No horizontal movement isCollapsed ? 0 : -100, // Move from top to bottom 0, // No depth movement ), child: widget.titleWidget, // Your title widget - ), + ) : widget.titleWidget, elevation: widget.elevation, backgroundColor: widget.backgroundColor, flexibleSpace: wrapsInFlexible(widget.backgroundWidget), @@ -989,14 +970,11 @@ class _AnimatedAppBarState extends State { foregroundColor: widget.foregroundColor, shadowColor: widget.shadowColor, toolbarHeight: widget.titleBarHeight, - // Only enable snap if floating is true - snap: widget.snap, ); } } enum AppBarBehavior { floating, - snap, pinned, } class ActionResponse { From 86bf83be8d906899672f043f140f471ee2dd055c Mon Sep 17 00:00:00 2001 From: Umair Manzoor Date: Tue, 25 Feb 2025 19:49:04 +0500 Subject: [PATCH 07/11] Code Reformatting --- modules/ensemble/lib/framework/view/page.dart | 124 ++++++++++-------- 1 file changed, 67 insertions(+), 57 deletions(-) diff --git a/modules/ensemble/lib/framework/view/page.dart b/modules/ensemble/lib/framework/view/page.dart index 4cb6eebee..d197780b0 100644 --- a/modules/ensemble/lib/framework/view/page.dart +++ b/modules/ensemble/lib/framework/view/page.dart @@ -289,9 +289,9 @@ class PageState extends State scrollableView: true, showNavigationIcon: pageModel.runtimeStyles?['showNavigationIcon'], ); - if (appBar is SliverAppBar || appBar is AnimatedAppBar) { - return appBar; - } + if (appBar is SliverAppBar || appBar is AnimatedAppBar) { + return appBar; + } } if (hasDrawer) { return const SliverAppBar(); @@ -359,22 +359,24 @@ class PageState extends State Color? shadowColor = Utils.getColor(evaluatedHeader?['shadowColor']); double? elevation = Utils.optionalInt(evaluatedHeader?['elevation'], min: 0)?.toDouble(); - AppBarBehavior behaviour = - Utils.getEnum(evaluatedHeader?['scrollMode'], AppBarBehavior.values); + ScrollMode scrollMode = + Utils.getEnum(evaluatedHeader?['scrollMode'], ScrollMode.values); final titleBarHeight = Utils.optionalInt(evaluatedHeader?['titleBarHeight'], min: 0) ?.toDouble() ?? kToolbarHeight; // animation - final animation = evaluatedHeader?['animation']!= null? EnsembleThemeManager.yamlToDart(evaluatedHeader?['animation']):null; + final animation = evaluatedHeader?['animation'] != null + ? EnsembleThemeManager.yamlToDart(evaluatedHeader?['animation']) + : null; bool animationEnabled = false; int? duration; Curve? curve; - if(animation != null){ + if (animation != null) { animationEnabled = Utils.getBool(animation!['enabled'], fallback: false); duration = Utils.getInt(animation!['duration'], fallback: 0); - curve = Utils.getCurve(animation!['curve']); + curve = Utils.getCurve(animation!['curve']); } // applicable only to Sliver scrolling double? flexibleMaxHeight = @@ -388,30 +390,31 @@ class PageState extends State } if (scrollableView) { - return AnimatedAppBar( scrollController: externalScrollController!, - automaticallyImplyLeading: - leadingWidget == null && showNavigationIcon != false, - leadingWidget: leadingWidget, - titleWidget: titleWidget, - centerTitle: centerTitle, - backgroundColor: backgroundColor, - surfaceTintColor: surfaceTintColor, - foregroundColor: color, - animated: animationEnabled, - // control the drop shadow on the header's bottom edge - elevation: elevation, - shadowColor: shadowColor, - - titleBarHeight: titleBarHeight, - curve: curve, - duration: duration, - backgroundWidget: backgroundWidget, - expandedBarHeight: flexibleMaxHeight, - collapsedBarHeight: flexibleMinHeight, - floating: behaviour == AppBarBehavior.floating, - pinned: behaviour == AppBarBehavior.pinned, - - ); + return AnimatedAppBar( scrollController: externalScrollController!, + automaticallyImplyLeading: + leadingWidget == null && showNavigationIcon != false, + leadingWidget: leadingWidget, + titleWidget: titleWidget, + centerTitle: centerTitle, + backgroundColor: backgroundColor, + surfaceTintColor: surfaceTintColor, + foregroundColor: color, + animated: animationEnabled, + + // control the drop shadow on the header's bottom edge + elevation: elevation, + shadowColor: shadowColor, + + titleBarHeight: titleBarHeight, + curve: curve, + duration: duration, + backgroundWidget: backgroundWidget, + expandedBarHeight: flexibleMaxHeight, + collapsedBarHeight: flexibleMinHeight, + floating: scrollMode == ScrollMode.floating, + pinned: scrollMode == ScrollMode.pinned, + + ); } else { return AppBar( @@ -883,33 +886,34 @@ class AnimatedAppBar extends StatefulWidget { final animated; final curve; final duration; - AnimatedAppBar({Key? key, - this.automaticallyImplyLeading, - this.leadingWidget, - this.titleWidget, - this.centerTitle, - this.backgroundColor, - this.surfaceTintColor, - this.foregroundColor, - this.elevation, - this.shadowColor, - this.titleBarHeight, - this.backgroundWidget, - this.animated, - this.floating, - this.pinned, - - this.collapsedBarHeight, - this.expandedBarHeight, required this.scrollController, - this.curve, - this.duration}) : super(key: key); + AnimatedAppBar( + {Key? key, + this.automaticallyImplyLeading, + this.leadingWidget, + this.titleWidget, + this.centerTitle, + this.backgroundColor, + this.surfaceTintColor, + this.foregroundColor, + this.elevation, + this.shadowColor, + this.titleBarHeight, + this.backgroundWidget, + this.animated, + this.floating, + this.pinned, + this.collapsedBarHeight, + this.expandedBarHeight, + required this.scrollController, + this.curve, + this.duration}) + : super(key: key); @override _AnimatedAppBarState createState() => _AnimatedAppBarState(); } class _AnimatedAppBarState extends State { - bool isCollapsed = false; @override @@ -920,7 +924,8 @@ class _AnimatedAppBarState extends State { void _updateCollapseState() { bool newState = widget.scrollController.hasClients && - widget.scrollController.offset > (widget.expandedBarHeight - widget.collapsedBarHeight); + widget.scrollController.offset > + (widget.expandedBarHeight - widget.collapsedBarHeight); if (newState != isCollapsed) { setState(() { @@ -934,6 +939,7 @@ class _AnimatedAppBarState extends State { widget.scrollController.removeListener(_updateCollapseState); super.dispose(); } + /// wraps the background in a FlexibleSpaceBar for automatic stretching and parallax effect. Widget? wrapsInFlexible(Widget? backgroundWidget) { if (backgroundWidget != null) { @@ -944,6 +950,7 @@ class _AnimatedAppBarState extends State { } return null; } + @override Widget build(BuildContext context) { return SliverAppBar( @@ -951,9 +958,11 @@ class _AnimatedAppBarState extends State { expandedHeight: widget.expandedBarHeight, pinned: widget.pinned, centerTitle: widget.centerTitle, - title: widget.animated ? AnimatedContainer( + title: widget.animated + ? AnimatedContainer( curve: widget.curve ?? Curves.easeIn, - duration: Duration(milliseconds: widget.duration ?? 300), // Animation duration + duration: Duration( + milliseconds: widget.duration ?? 300), // Animation duration transform: Matrix4.translationValues( 0, // No horizontal movement isCollapsed ? 0 : -100, // Move from top to bottom @@ -973,7 +982,8 @@ class _AnimatedAppBarState extends State { ); } } -enum AppBarBehavior { + +enum ScrollMode { floating, pinned, } From 64687c834ed4628fc363dd32ae37779958250149 Mon Sep 17 00:00:00 2001 From: Umair Manzoor Date: Tue, 25 Feb 2025 20:28:47 +0500 Subject: [PATCH 08/11] Restoring old format for irrelevant changes --- modules/ensemble/lib/framework/view/page.dart | 14 +++++--------- modules/ensemble/lib/page_model.dart | 16 ++++++++-------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/modules/ensemble/lib/framework/view/page.dart b/modules/ensemble/lib/framework/view/page.dart index d197780b0..a50cfdc8a 100644 --- a/modules/ensemble/lib/framework/view/page.dart +++ b/modules/ensemble/lib/framework/view/page.dart @@ -24,7 +24,6 @@ import 'package:ensemble/widget/helpers/controllers.dart'; import 'package:ensemble/widget/helpers/unfocus.dart'; import 'package:flutter/material.dart'; - class SinglePageController extends WidgetController { TextStyleComposite? _textStyle; int? maxLines; @@ -284,11 +283,9 @@ class PageState extends State /// create AppBar that is part of a CustomScrollView Widget? buildSliverAppBar(SinglePageModel pageModel, bool hasDrawer) { if (pageModel.headerModel != null) { - dynamic appBar = _buildAppBar( - pageModel.headerModel!, - scrollableView: true, - showNavigationIcon: pageModel.runtimeStyles?['showNavigationIcon'], - ); + dynamic appBar = _buildAppBar(pageModel.headerModel!, + scrollableView: true, + showNavigationIcon: pageModel.runtimeStyles?['showNavigationIcon']); if (appBar is SliverAppBar || appBar is AnimatedAppBar) { return appBar; } @@ -320,9 +317,7 @@ class PageState extends State /// fixed AppBar dynamic _buildAppBar(HeaderModel headerModel, - {required bool scrollableView, - bool? showNavigationIcon, - }) { + {required bool scrollableView, bool? showNavigationIcon}) { Widget? titleWidget; @@ -616,6 +611,7 @@ class PageState extends State if (appBar != null) { slivers.add(appBar); } + // body slivers.add(SliverToBoxAdapter( child: getBody(appBar != null), diff --git a/modules/ensemble/lib/page_model.dart b/modules/ensemble/lib/page_model.dart index e910ec03e..14fe9bd95 100644 --- a/modules/ensemble/lib/page_model.dart +++ b/modules/ensemble/lib/page_model.dart @@ -382,6 +382,7 @@ class SinglePageModel extends PageModel with HasStyles { background = ViewUtil.buildModel( headerData['flexibleBackground'], customViewDefinitions); } + styles = EnsembleThemeManager.yamlToDart(headerData['styles']); classList = HasStyles.toClassList( headerData[ViewUtil.classNameAttribute] as String?); @@ -394,14 +395,12 @@ class SinglePageModel extends PageModel with HasStyles { leadingWidget != null || classList != null) { headerModel = HeaderModel( - - titleText: titleText, - titleWidget: titleWidget, - flexibleBackground: background, - leadingWidget: leadingWidget, - inlineStyles: styles, - classList: classList, - ); + titleText: titleText, + titleWidget: titleWidget, + flexibleBackground: background, + leadingWidget: leadingWidget, + inlineStyles: styles, + classList: classList); } } @@ -556,6 +555,7 @@ class HeaderModel extends Object with HasStyles { WidgetModel? flexibleBackground; } + class FooterItems extends Object with HasStyles { final List children; Map? inlineStyles; From e4ee38045966827b92d28e9dc94e3e76ebf48792 Mon Sep 17 00:00:00 2001 From: Umair Manzoor <76640178+Umair-Manzoor-47@users.noreply.github.com> Date: Tue, 25 Feb 2025 20:40:25 +0500 Subject: [PATCH 09/11] undoing unnecessary space page_model.dart --- modules/ensemble/lib/page_model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ensemble/lib/page_model.dart b/modules/ensemble/lib/page_model.dart index 14fe9bd95..845e5f523 100644 --- a/modules/ensemble/lib/page_model.dart +++ b/modules/ensemble/lib/page_model.dart @@ -398,7 +398,7 @@ class SinglePageModel extends PageModel with HasStyles { titleText: titleText, titleWidget: titleWidget, flexibleBackground: background, - leadingWidget: leadingWidget, + leadingWidget: leadingWidget, inlineStyles: styles, classList: classList); } From 92ce92b3bf9a85b258f9af5020c2c3b587ea0c87 Mon Sep 17 00:00:00 2001 From: Umair Manzoor <76640178+Umair-Manzoor-47@users.noreply.github.com> Date: Tue, 25 Feb 2025 20:41:57 +0500 Subject: [PATCH 10/11] Update page_model.dart From 8d9e943fe2b3a2721edfe6243e5818f007abb091 Mon Sep 17 00:00:00 2001 From: Umair Manzoor <76640178+Umair-Manzoor-47@users.noreply.github.com> Date: Tue, 25 Feb 2025 20:43:47 +0500 Subject: [PATCH 11/11] Update page_model.dart