From 6ccccb36627ef911835e9367ff9006ec440bfe23 Mon Sep 17 00:00:00 2001 From: Baig Date: Thu, 18 May 2023 15:22:17 +0500 Subject: [PATCH 01/12] Update new_gradient_app_bar.dart using chatgpt --- lib/new_gradient_app_bar.dart | 1206 +++------------------------------ 1 file changed, 103 insertions(+), 1103 deletions(-) diff --git a/lib/new_gradient_app_bar.dart b/lib/new_gradient_app_bar.dart index c6b3e4c..f87f641 100644 --- a/lib/new_gradient_app_bar.dart +++ b/lib/new_gradient_app_bar.dart @@ -8,10 +8,8 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; -const double _kLeadingWidth = - kToolbarHeight; // So the leading button is square. +const double _kLeadingWidth = kToolbarHeight; -// Bottom justify the kToolbarHeight child which may overflow the top. class _ToolbarContainerLayout extends SingleChildLayoutDelegate { const _ToolbarContainerLayout(); @@ -34,134 +32,7 @@ class _ToolbarContainerLayout extends SingleChildLayoutDelegate { bool shouldRelayout(_ToolbarContainerLayout oldDelegate) => false; } -// TODO(eseidel): Toolbar needs to change size based on orientation: -// https://material.io/design/components/app-bars-top.html#specs -// Mobile Landscape: 48dp -// Mobile Portrait: 56dp -// Tablet/Desktop: 64dp - -/// A material design app bar. -/// -/// An app bar consists of a toolbar and potentially other widgets, such as a -/// [TabBar] and a [FlexibleSpaceBar]. App bars typically expose one or more -/// common [actions] with [IconButton]s which are optionally followed by a -/// [PopupMenuButton] for less common operations (sometimes called the "overflow -/// menu"). -/// -/// App bars are typically used in the [Scaffold.appBar] property, which places -/// the app bar as a fixed-height widget at the top of the screen. For a scrollable -/// app bar, see [SliverNewGradientAppBar], which embeds an [NewGradientAppBar] in a sliver for use in -/// a [CustomScrollView]. -/// -/// When not used as [Scaffold.appBar], or when wrapped in a [Hero], place the app -/// bar in a [MediaQuery] to take care of the padding around the content of the -/// app bar if needed, as the padding will not be handled by [Scaffold]. -/// -/// The GradientAppBar displays the toolbar widgets, [leading], [title], and [actions], -/// above the [bottom] (if any). The [bottom] is usually used for a [TabBar]. If -/// a [flexibleSpace] widget is specified then it is stacked behind the toolbar -/// and the bottom widget. The following diagram shows where each of these slots -/// appears in the toolbar when the writing language is left-to-right (e.g. -/// English): -/// -/// ![The leading widget is in the top left, the actions are in the top right, -/// the title is between them. The bottom is, naturally, at the bottom, and the -/// flexibleSpace is behind all of them.](https://flutter.github.io/assets-for-api-docs/assets/material/app_bar.png) -/// -/// If the [leading] widget is omitted, but the [NewGradientAppBar] is in a [Scaffold] with -/// a [Drawer], then a button will be inserted to open the drawer. Otherwise, if -/// the nearest [Navigator] has any previous routes, a [BackButton] is inserted -/// instead. This behavior can be turned off by setting the [automaticallyImplyLeading] -/// to false. In that case a null leading widget will result in the middle/title widget -/// stretching to start. -/// -/// {@tool snippet --template=stateless_widget_material} -/// -/// This sample shows an [NewGradientAppBar] with two simple actions. The first action -/// opens a [SnackBar], while the second action navigates to a new page. -/// -/// ```dart preamble -/// final GlobalKey scaffoldKey = GlobalKey(); -/// final SnackBar snackBar = const SnackBar(content: Text('Showing Snackbar')); -/// -/// void openPage(BuildContext context) { -/// Navigator.push(context, MaterialPageRoute( -/// builder: (BuildContext context) { -/// return Scaffold( -/// appBar: GradientAppBar( -/// title: const Text('Next page'), -/// ), -/// body: const Center( -/// child: Text( -/// 'This is the next page', -/// style: TextStyle(fontSize: 24), -/// ), -/// ), -/// ); -/// }, -/// )); -/// } -/// ``` -/// -/// ```dart -/// Widget build(BuildContext context) { -/// return Scaffold( -/// key: scaffoldKey, -/// appBar: GradientAppBar( -/// title: const Text('GradientAppBar Demo'), -/// actions: [ -/// IconButton( -/// icon: const Icon(Icons.add_alert), -/// tooltip: 'Show Snackbar', -/// onPressed: () { -/// scaffoldKey.currentState.showSnackBar(snackBar); -/// }, -/// ), -/// IconButton( -/// icon: const Icon(Icons.navigate_next), -/// tooltip: 'Next page', -/// onPressed: () { -/// openPage(context); -/// }, -/// ), -/// ], -/// ), -/// body: const Center( -/// child: Text( -/// 'This is the home page', -/// style: TextStyle(fontSize: 24), -/// ), -/// ), -/// ); -/// } -/// ``` -/// {@end-tool} -/// -/// See also: -/// -/// * [Scaffold], which displays the [NewGradientAppBar] in its [Scaffold.appBar] slot. -/// * [SliverNewGradientAppBar], which uses [NewGradientAppBar] to provide a flexible app bar that -/// can be used in a [CustomScrollView]. -/// * [TabBar], which is typically placed in the [bottom] slot of the [NewGradientAppBar] -/// if the screen has multiple pages arranged in tabs. -/// * [IconButton], which is used with [actions] to show buttons on the app bar. -/// * [PopupMenuButton], to show a popup menu on the app bar, via [actions]. -/// * [FlexibleSpaceBar], which is used with [flexibleSpace] when the app bar -/// can expand and collapse. -/// * class NewGradientAppBar extends StatefulWidget implements PreferredSizeWidget { - /// Creates a material design app bar. - /// - /// The arguments [primary], [toolbarOpacity], [bottomOpacity] - /// and [automaticallyImplyLeading] must not be null. Additionally, if - /// [elevation] is specified, it must be non-negative. - /// - /// If [elevation], [brightness], [iconTheme], - /// [actionsIconTheme], or [textTheme] are null, then their [GradientAppBarTheme] - /// values will be used. If the corresponding [GradientAppBarTheme] property is null, - /// then the default specified in the property's documentation will be used. - /// - /// Typically used in the [Scaffold.appBar] property. NewGradientAppBar({ Key? key, this.leading, @@ -176,192 +47,33 @@ class NewGradientAppBar extends StatefulWidget implements PreferredSizeWidget { this.brightness, this.iconTheme, this.actionsIconTheme, - this.textTheme, this.primary = true, this.centerTitle, this.titleSpacing = NavigationToolbar.kMiddleSpacing, this.toolbarOpacity = 1.0, this.bottomOpacity = 1.0, }) : assert(elevation == null || elevation >= 0.0), - preferredSize = Size.fromHeight( - kToolbarHeight + (bottom?.preferredSize.height ?? 0.0)), + preferredSize = Size.fromHeight(kToolbarHeight + (bottom?.preferredSize.height ?? 0.0)), super(key: key); - /// A widget to display before the [title]. - /// - /// If this is null and [automaticallyImplyLeading] is set to true, the - /// [NewGradientAppBar] will imply an appropriate widget. For example, if the [NewGradientAppBar] is - /// in a [Scaffold] that also has a [Drawer], the [Scaffold] will fill this - /// widget with an [IconButton] that opens the drawer (using [Icons.menu]). If - /// there's no [Drawer] and the parent [Navigator] can go back, the [NewGradientAppBar] - /// will use a [BackButton] that calls [Navigator.maybePop]. - /// - /// {@tool sample} - /// - /// The following code shows how the drawer button could be manually specified - /// instead of relying on [automaticallyImplyLeading]: - /// - /// ```dart - /// GradientAppBar( - /// leading: Builder( - /// builder: (BuildContext context) { - /// return IconButton( - /// icon: const Icon(Icons.menu), - /// onPressed: () { Scaffold.of(context).openDrawer(); }, - /// tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip, - /// ); - /// }, - /// ), - /// ) - /// ``` - /// {@end-tool} - /// - /// The [Builder] is used in this example to ensure that the `context` refers - /// to that part of the subtree. That way this code snippet can be used even - /// inside the very code that is creating the [Scaffold] (in which case, - /// without the [Builder], the `context` wouldn't be able to see the - /// [Scaffold], since it would refer to an ancestor of that widget). - /// - /// See also: - /// - /// * [Scaffold.appBar], in which an [NewGradientAppBar] is usually placed. - /// * [Scaffold.drawer], in which the [Drawer] is usually placed. final Widget? leading; - - /// Controls whether we should try to imply the leading widget if null. - /// - /// If true and [leading] is null, automatically try to deduce what the leading - /// widget should be. If false and [leading] is null, leading space is given to [title]. - /// If leading widget is not null, this parameter has no effect. final bool automaticallyImplyLeading; - - /// The primary widget displayed in the appbar. - /// - /// Typically a [Text] widget containing a description of the current contents - /// of the app. final Widget? title; - - /// Widgets to display after the [title] widget. - /// - /// Typically these widgets are [IconButton]s representing common operations. - /// For less common operations, consider using a [PopupMenuButton] as the - /// last action. final List? actions; - - /// This widget is stacked behind the toolbar and the tab bar. It's height will - /// be the same as the app bar's overall height. - /// - /// A flexible space isn't actually flexible unless the [NewGradientAppBar]'s container - /// changes the [NewGradientAppBar]'s size. A [SliverNewGradientAppBar] in a [CustomScrollView] - /// changes the [NewGradientAppBar]'s height when scrolled. - /// - /// Typically a [FlexibleSpaceBar]. See [FlexibleSpaceBar] for details. final Widget? flexibleSpace; - - /// This widget appears across the bottom of the app bar. - /// - /// Typically a [TabBar]. Only widgets that implement [PreferredSizeWidget] can - /// be used at the bottom of an app bar. - /// - /// See also: - /// - /// * [PreferredSize], which can be used to give an arbitrary widget a preferred size. final PreferredSizeWidget? bottom; - - /// The z-coordinate at which to place this app bar relative to its parent. - /// - /// This controls the size of the shadow below the app bar. - /// - /// The value is non-negative. - /// - /// If this property is null, then [ThemeData.appBarTheme.elevation] is used, - /// if that is also null, the default value is 4, the appropriate elevation - /// for app bars. final double? elevation; - - /// The material's shape as well its shadow. - /// - /// A shadow is only displayed if the [elevation] is greater than - /// zero. final ShapeBorder? shape; - - /// The color to use for the app bar's material. Typically this should be set - /// along with [brightness], [iconTheme], [textTheme]. - /// - /// If this property is null, then [ThemeData.appBarTheme.color] is used, - /// if that is also null, then [ThemeData.primaryColor] is used. final Gradient? gradient; - - /// The gradient displayed at the appbar. - /// - /// If this property is null, then [ThemeData.appBarTheme.brightness] is used, - /// if that is also null, then [ThemeData.primaryColorBrightness] is used. final Brightness? brightness; - - /// The color, opacity, and size to use for app bar icons. Typically this - /// is set along with [backgroundColor], [brightness], [textTheme]. - /// - /// If this property is null, then [ThemeData.appBarTheme.iconTheme] is used, - /// if that is also null, then [ThemeData.primaryIconTheme] is used. final IconThemeData? iconTheme; - - /// The color, opacity, and size to use for the icons that appear in the app - /// bar's [actions]. This should only be used when the [actions] should be - /// themed differently than the icon that appears in the app bar's [leading] - /// widget. - /// - /// If this property is null, then [ThemeData.appBarTheme.actionsIconTheme] is - /// used, if that is also null, then this falls back to [iconTheme]. final IconThemeData? actionsIconTheme; - - /// The typographic styles to use for text in the app bar. Typically this is - /// set along with [brightness] [backgroundColor], [iconTheme]. - /// - /// If this property is null, then [ThemeData.appBarTheme.textTheme] is used, - /// if that is also null, then [ThemeData.primaryTextTheme] is used. - final TextTheme? textTheme; - - /// Whether this app bar is being displayed at the top of the screen. - /// - /// If true, the app bar's toolbar elements and [bottom] widget will be - /// padded on top by the height of the system status bar. The layout - /// of the [flexibleSpace] is not affected by the [primary] property. final bool primary; - - /// Whether the title should be centered. - /// - /// Defaults to being adapted to the current [TargetPlatform]. final bool? centerTitle; - - /// The spacing around [title] content on the horizontal axis. This spacing is - /// applied even if there is no [leading] content or [actions]. If you want - /// [title] to take all the space available, set this value to 0.0. - /// - /// Defaults to [NavigationToolbar.kMiddleSpacing]. final double titleSpacing; - - /// How opaque the toolbar part of the app bar is. - /// - /// A value of 1.0 is fully opaque, and a value of 0.0 is fully transparent. - /// - /// Typically, this value is not changed from its default value (1.0). It is - /// used by [SliverNewGradientAppBar] to animate the opacity of the toolbar when the app - /// bar is scrolled. final double toolbarOpacity; - - /// How opaque the bottom part of the app bar is. - /// - /// A value of 1.0 is fully opaque, and a value of 0.0 is fully transparent. - /// - /// Typically, this value is not changed from its default value (1.0). It is - /// used by [SliverNewGradientAppBar] to animate the opacity of the toolbar when the app - /// bar is scrolled. final double bottomOpacity; - /// A size whose height is the sum of [kToolbarHeight] and the [bottom] widget's - /// preferred height. - /// - /// [Scaffold] uses this this size to set its app bar's height. @override final Size preferredSize; @@ -403,832 +115,120 @@ class _NewGradientAppBarState extends State { assert(!widget.primary || debugCheckHasMediaQuery(context)); assert(debugCheckHasMaterialLocalizations(context)); final ThemeData themeData = Theme.of(context); - final AppBarTheme appBarTheme = AppBarTheme.of(context); - final ScaffoldState scaffold = Scaffold.of(context); final ModalRoute? parentRoute = ModalRoute.of(context); - - final bool hasDrawer = scaffold.hasDrawer; - final bool hasEndDrawer = scaffold.hasEndDrawer; - final bool canPop = parentRoute?.canPop ?? false; - final bool useCloseButton = - parentRoute is PageRoute && parentRoute.fullscreenDialog; - - IconThemeData overallIconTheme = - widget.iconTheme ?? appBarTheme.iconTheme ?? themeData.primaryIconTheme; - IconThemeData actionsIconTheme = widget.actionsIconTheme ?? - appBarTheme.actionsIconTheme ?? - overallIconTheme; - TextStyle centerStyle = (widget.textTheme?.headline6 ?? - appBarTheme.textTheme?.headline6 ?? - themeData.primaryTextTheme.headline6)!; - TextStyle? sideStyle = widget.textTheme?.bodyText2 ?? - appBarTheme.textTheme?.bodyText2 ?? - themeData.primaryTextTheme.bodyText2; - - if (widget.toolbarOpacity != 1.0) { - final double opacity = - const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn) - .transform(widget.toolbarOpacity); - if (centerStyle.color != null) - centerStyle = centerStyle.copyWith( - color: centerStyle.color!.withOpacity(opacity)); - if (sideStyle?.color != null) - sideStyle = - sideStyle!.copyWith(color: sideStyle.color!.withOpacity(opacity)); - overallIconTheme = overallIconTheme.copyWith( - opacity: opacity * (overallIconTheme.opacity ?? 1.0)); - actionsIconTheme = actionsIconTheme.copyWith( - opacity: opacity * (actionsIconTheme.opacity ?? 1.0)); - } - - Widget? leading = widget.leading; - if (leading == null && widget.automaticallyImplyLeading) { - if (hasDrawer) { - leading = IconButton( - icon: const Icon(Icons.menu), - onPressed: _handleDrawerButton, - tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip, - ); - } else { - if (canPop) - leading = useCloseButton ? const CloseButton() : const BackButton(); - } - } - if (leading != null) { - leading = ConstrainedBox( - constraints: const BoxConstraints.tightFor(width: _kLeadingWidth), - child: leading, + final bool hasDrawer = widget.automaticallyImplyLeading && parentRoute?.canPop == true; + final bool useCloseButton = !widget.primary; + final bool effectiveCenterTitle = widget._getEffectiveCenterTitle(themeData); + + IconThemeData appBarIconTheme = + widget.iconTheme ?? themeData.primaryIconTheme; + IconThemeData actionsIconTheme = + widget.actionsIconTheme ?? themeData.primaryIconTheme; + TextStyle? appBarTextStyle = themeData.primaryTextTheme.headline6; + TextStyle? actionsTextStyle = themeData.primaryTextTheme.subtitle1; + + if (widget.brightness != null) { + final bool isDark = widget.brightness == Brightness.dark; + appBarIconTheme = appBarIconTheme.copyWith( + opacity: isDark ? 0.7 : 1.0, ); - } - - Widget? title = widget.title; - if (title != null) { - bool? namesRoute; - switch (defaultTargetPlatform) { - case TargetPlatform.android: - case TargetPlatform.fuchsia: - namesRoute = true; - break; - case TargetPlatform.iOS: - case TargetPlatform.linux: - case TargetPlatform.macOS: - case TargetPlatform.windows: - break; - } - title = DefaultTextStyle( - style: centerStyle, - softWrap: false, - overflow: TextOverflow.ellipsis, - child: Semantics( - namesRoute: namesRoute, - child: title, - header: true, - ), + actionsIconTheme = actionsIconTheme.copyWith( + opacity: isDark ? 0.7 : 1.0, ); - } - - Widget? actions; - if (widget.actions != null && widget.actions!.isNotEmpty) { - actions = Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: (widget.actions)!, + appBarTextStyle = appBarTextStyle!.copyWith( + color: isDark ? Colors.white : Colors.black87, ); - } else if (hasEndDrawer) { - actions = IconButton( - icon: const Icon(Icons.menu), - onPressed: _handleDrawerButtonEnd, - tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip, + actionsTextStyle = actionsTextStyle!.copyWith( + color: isDark ? Colors.white : Colors.black87, ); } - // Allow the trailing actions to have their own theme if necessary. - if (actions != null) { - actions = IconTheme.merge( - data: actionsIconTheme, - child: actions, - ); - } - - final Widget toolbar = NavigationToolbar( - leading: leading, - middle: title, - trailing: actions, - centerMiddle: (widget._getEffectiveCenterTitle(themeData))!, - middleSpacing: widget.titleSpacing, - ); - - // If the toolbar is allocated less than kToolbarHeight make it - // appear to scroll upwards within its shrinking container. - Widget appBar = ClipRect( - child: CustomSingleChildLayout( - delegate: const _ToolbarContainerLayout(), - child: IconTheme.merge( - data: overallIconTheme, - child: DefaultTextStyle( - style: (sideStyle)!, - child: toolbar, - ), - ), - ), - ); - if (widget.bottom != null) { - appBar = Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: ConstrainedBox( - constraints: const BoxConstraints(maxHeight: kToolbarHeight), - child: appBar, + final SystemUiOverlayStyle overlayStyle = + themeData.appBarTheme.systemOverlayStyle ?? + SystemUiOverlayStyle.light; + final bool backwardsCompatibility = themeData.appBarTheme + .backwardsCompatibility ??= + false; + + return AnnotatedRegion( + value: backwardsCompatibility + ? overlayStyle.copyWith( + statusBarBrightness: widget.brightness, + ) + : overlayStyle, + child: Semantics( + container: true, + explicitChildNodes: true, + child: Stack( + children: [ + if (widget.flexibleSpace != null) + Positioned.fill(child: widget.flexibleSpace!), + PositionedDirectional( + top: 0.0, + start: effectiveCenterTitle ? 0.0 : null, + end: effectiveCenterTitle ? 0.0 : null, + child: AppBar( + backgroundColor: Colors.transparent, + toolbarOpacity: widget.toolbarOpacity, + bottomOpacity: widget.bottomOpacity, + elevation: widget.elevation ?? _defaultElevation, + shape: widget.shape, + leading: widget.leading ?? + (hasDrawer ? null : (useCloseButton ? null : Container())), + title: widget.title != null + ? DefaultTextStyle( + style: appBarTextStyle!, + child: Semantics( + namesRoute: true, + child: widget.title!, + header: true, + ), + ) + : null, + actions: widget.actions != null + ? _buildActions(actionsTextStyle!, actionsIconTheme) + : null, + centerTitle: effectiveCenterTitle, + titleSpacing: widget.titleSpacing, + toolbarOpacity: widget.toolbarOpacity, + bottomOpacity: widget.bottomOpacity, + backwardsCompatibility: backwardsCompatibility, + titleTextStyle: appBarTextStyle, + systemOverlayStyle: overlayStyle, + ), ), - ), - widget.bottomOpacity == 1.0 - ? widget.bottom! - : Opacity( - opacity: - const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn) - .transform(widget.bottomOpacity), - child: widget.bottom!, - ), - ], - ); - } - - // The padding applies to the toolbar and tabbar, not the flexible space. - if (widget.primary) { - appBar = SafeArea( - top: true, - child: appBar, - ); - } - - appBar = Align( - alignment: Alignment.topCenter, - child: appBar, - ); - - if (widget.flexibleSpace != null) { - appBar = Stack( - fit: StackFit.passthrough, - children: [ - widget.flexibleSpace!, - appBar, - ], - ); - } - final Brightness brightness = widget.brightness ?? - appBarTheme.brightness ?? - themeData.primaryColorBrightness; - final SystemUiOverlayStyle overlayStyle = brightness == Brightness.dark - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark; - - return Semantics( - container: true, - child: AnnotatedRegion( - value: overlayStyle, - child: Material( - color: appBarTheme.color ?? themeData.primaryColor, - elevation: - widget.elevation ?? appBarTheme.elevation ?? _defaultElevation, - shape: widget.shape, - child: Container( - decoration: BoxDecoration(gradient: widget.gradient), - child: Semantics( - explicitChildNodes: true, - child: appBar, + if (widget.bottom != null) + Positioned( + left: 0.0, + right: 0.0, + bottom: 0.0, + child: widget.bottom!, ), - )), - ), - ); - } -} - -class _FloatingGradientAppBar extends StatefulWidget { - const _FloatingGradientAppBar({Key? key, this.child}) : super(key: key); - - final Widget? child; - - @override - _FloatingGradientAppBarState createState() => _FloatingGradientAppBarState(); -} - -// A wrapper for the widget created by _SliverGradientAppBarDelegate that starts and -// stops the floating app bar's snap-into-view or snap-out-of-view animation. -class _FloatingGradientAppBarState extends State<_FloatingGradientAppBar> { - ScrollPosition? _position; - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - if (_position != null) - _position!.isScrollingNotifier.removeListener(_isScrollingListener); - _position = Scrollable.of(context)?.position; - if (_position != null) - _position!.isScrollingNotifier.addListener(_isScrollingListener); - } - - @override - void dispose() { - if (_position != null) - _position!.isScrollingNotifier.removeListener(_isScrollingListener); - super.dispose(); - } - - RenderSliverFloatingPersistentHeader? _headerRenderer() { - return context - .findAncestorRenderObjectOfType(); - } - - void _isScrollingListener() { - // When a scroll stops, then maybe snap the appbar into view. - // Similarly, when a scroll starts, then maybe stop the snap animation. - final RenderSliverFloatingPersistentHeader? header = _headerRenderer(); - if (_position!.isScrollingNotifier.value) - header?.maybeStopSnapAnimation(_position!.userScrollDirection); - else - header?.maybeStartSnapAnimation(_position!.userScrollDirection); - } - - @override - Widget build(BuildContext context) => widget.child!; -} - -class _SliverGradientAppBarDelegate extends SliverPersistentHeaderDelegate { - _SliverGradientAppBarDelegate({ - required this.leading, - required this.automaticallyImplyLeading, - required this.title, - required this.actions, - required this.flexibleSpace, - required this.bottom, - required this.elevation, - required this.forceElevated, - required this.gradient, - required this.brightness, - required this.iconTheme, - required this.actionsIconTheme, - required this.textTheme, - required this.primary, - required this.centerTitle, - required this.titleSpacing, - required this.expandedHeight, - required this.collapsedHeight, - required this.topPadding, - required this.floating, - required this.pinned, - required this.snapConfiguration, - required this.shape, - }) : assert(primary || topPadding == 0.0), - _bottomHeight = bottom.preferredSize.height; - - final Widget leading; - final bool automaticallyImplyLeading; - final Widget title; - final List actions; - final Widget flexibleSpace; - final PreferredSizeWidget bottom; - final double elevation; - final bool forceElevated; - final Gradient gradient; - final Brightness brightness; - final IconThemeData iconTheme; - final IconThemeData actionsIconTheme; - final TextTheme textTheme; - final bool primary; - final bool centerTitle; - final double titleSpacing; - final double expandedHeight; - final double collapsedHeight; - final double topPadding; - final bool floating; - final bool pinned; - final ShapeBorder shape; - - final double _bottomHeight; - - @override - double get minExtent => collapsedHeight; - - @override - double get maxExtent => math.max(topPadding + expandedHeight, minExtent); - - @override - final FloatingHeaderSnapConfiguration snapConfiguration; - - @override - Widget build( - BuildContext context, double shrinkOffset, bool overlapsContent) { - final double visibleMainHeight = maxExtent - shrinkOffset - topPadding; - - // Truth table for `toolbarOpacity`: - // pinned | floating | bottom != null || opacity - // ---------------------------------------------- - // 0 | 0 | 0 || fade - // 0 | 0 | 1 || fade - // 0 | 1 | 0 || fade - // 0 | 1 | 1 || fade - // 1 | 0 | 0 || 1.0 - // 1 | 0 | 1 || 1.0 - // 1 | 1 | 0 || 1.0 - // 1 | 1 | 1 || fade - final double toolbarOpacity = !pinned || (floating) - ? ((visibleMainHeight - _bottomHeight) / kToolbarHeight).clamp(0.0, 1.0) - : 1.0; - - final Widget appBar = FlexibleSpaceBar.createSettings( - minExtent: minExtent, - maxExtent: maxExtent, - currentExtent: math.max(minExtent, maxExtent - shrinkOffset), - toolbarOpacity: toolbarOpacity, - child: NewGradientAppBar( - leading: leading, - automaticallyImplyLeading: automaticallyImplyLeading, - title: title, - actions: actions, - // ignore: unnecessary_null_comparison - flexibleSpace: (title == null && flexibleSpace != null) - ? Semantics(child: flexibleSpace, header: true) - : flexibleSpace, - bottom: bottom, - elevation: forceElevated || - overlapsContent || - (pinned && shrinkOffset > maxExtent - minExtent) - ? elevation - : 0.0, - gradient: gradient, - brightness: brightness, - iconTheme: iconTheme, - actionsIconTheme: actionsIconTheme, - textTheme: textTheme, - primary: primary, - centerTitle: centerTitle, - titleSpacing: titleSpacing, - shape: shape, - toolbarOpacity: toolbarOpacity, - bottomOpacity: - pinned ? 1.0 : (visibleMainHeight / _bottomHeight).clamp(0.0, 1.0), + ], + ), ), ); - return floating ? _FloatingGradientAppBar(child: appBar) : appBar; - } - - @override - bool shouldRebuild(covariant _SliverGradientAppBarDelegate oldDelegate) { - return leading != oldDelegate.leading || - automaticallyImplyLeading != oldDelegate.automaticallyImplyLeading || - title != oldDelegate.title || - actions != oldDelegate.actions || - flexibleSpace != oldDelegate.flexibleSpace || - bottom != oldDelegate.bottom || - _bottomHeight != oldDelegate._bottomHeight || - elevation != oldDelegate.elevation || - gradient != oldDelegate.gradient || - brightness != oldDelegate.brightness || - iconTheme != oldDelegate.iconTheme || - actionsIconTheme != oldDelegate.actionsIconTheme || - textTheme != oldDelegate.textTheme || - primary != oldDelegate.primary || - centerTitle != oldDelegate.centerTitle || - titleSpacing != oldDelegate.titleSpacing || - expandedHeight != oldDelegate.expandedHeight || - topPadding != oldDelegate.topPadding || - pinned != oldDelegate.pinned || - floating != oldDelegate.floating || - snapConfiguration != oldDelegate.snapConfiguration; - } - - @override - String toString() { - return '${describeIdentity(this)}(topPadding: ${topPadding.toStringAsFixed(1)}, bottomHeight: ${_bottomHeight.toStringAsFixed(1)}, ...)'; } -} - -/// A material design app bar that integrates with a [CustomScrollView]. -/// -/// An app bar consists of a toolbar and potentially other widgets, such as a -/// [TabBar] and a [FlexibleSpaceBar]. App bars typically expose one or more -/// common actions with [IconButton]s which are optionally followed by a -/// [PopupMenuButton] for less common operations. -/// -/// {@youtube 560 315 https://www.youtube.com/watch?v=R9C5KMJKluE} -/// -/// Sliver app bars are typically used as the first child of a -/// [CustomScrollView], which lets the app bar integrate with the scroll view so -/// that it can vary in height according to the scroll offset or float above the -/// other content in the scroll view. For a fixed-height app bar at the top of -/// the screen see [NewGradientAppBar], which is used in the [Scaffold.appBar] slot. -/// -/// The GradientAppBar displays the toolbar widgets, [leading], [title], and -/// [actions], above the [bottom] (if any). If a [flexibleSpace] widget is -/// specified then it is stacked behind the toolbar and the bottom widget. -/// -/// {@tool sample} -/// -/// This is an example that could be included in a [CustomScrollView]'s -/// [CustomScrollView.slivers] list: -/// -/// ```dart -/// SliverGradientAppBar( -/// expandedHeight: 150.0, -/// flexibleSpace: const FlexibleSpaceBar( -/// title: Text('Available seats'), -/// ), -/// actions: [ -/// IconButton( -/// icon: const Icon(Icons.add_circle), -/// tooltip: 'Add new entry', -/// onPressed: () { /* ... */ }, -/// ), -/// ] -/// ) -/// ``` -/// {@end-tool} -/// -/// ## Animated Examples -/// -/// The following animations show how app bars with different configurations -/// behave when a user scrolls up and then down again. -/// -/// * App bar with [floating]: false, [pinned]: false, [snap]: false: -/// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar.mp4} -/// -/// * App bar with [floating]: true, [pinned]: false, [snap]: false: -/// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_floating.mp4} -/// -/// * App bar with [floating]: true, [pinned]: false, [snap]: true: -/// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_floating_snap.mp4} -/// -/// * App bar with [floating]: true, [pinned]: true, [snap]: false: -/// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_pinned_floating.mp4} -/// -/// * App bar with [floating]: true, [pinned]: true, [snap]: true: -/// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_pinned_floating_snap.mp4} -/// -/// * App bar with [floating]: false, [pinned]: true, [snap]: false: -/// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_pinned.mp4} -/// -/// The property [snap] can only be set to true if [floating] is also true. -/// -/// See also: -/// -/// * [CustomScrollView], which integrates the [SliverNewGradientAppBar] into its -/// scrolling. -/// * [NewGradientAppBar], which is a fixed-height app bar for use in [Scaffold.appBar]. -/// * [TabBar], which is typically placed in the [bottom] slot of the [NewGradientAppBar] -/// if the screen has multiple pages arranged in tabs. -/// * [IconButton], which is used with [actions] to show buttons on the app bar. -/// * [PopupMenuButton], to show a popup menu on the app bar, via [actions]. -/// * [FlexibleSpaceBar], which is used with [flexibleSpace] when the app bar -/// can expand and collapse. -/// * -class SliverNewGradientAppBar extends StatefulWidget { - /// Creates a material design app bar that can be placed in a [CustomScrollView]. - /// - /// The arguments [forceElevated], [primary], [floating], [pinned], [snap] - /// and [automaticallyImplyLeading] must not be null. - const SliverNewGradientAppBar({ - Key? key, - this.leading, - this.automaticallyImplyLeading = true, - this.title, - this.actions, - this.flexibleSpace, - this.bottom, - this.elevation, - this.forceElevated = false, - this.gradient, - this.brightness, - this.iconTheme, - this.actionsIconTheme, - this.textTheme, - this.primary = true, - this.centerTitle, - this.titleSpacing = NavigationToolbar.kMiddleSpacing, - this.expandedHeight, - this.floating = false, - this.pinned = false, - this.snap = false, - this.shape, - }) : assert(floating || !snap, - 'The "snap" argument only makes sense for floating app bars.'), - super(key: key); - - /// A widget to display before the [title]. - /// - /// If this is null and [automaticallyImplyLeading] is set to true, the [NewGradientAppBar] will - /// imply an appropriate widget. For example, if the [NewGradientAppBar] is in a [Scaffold] - /// that also has a [Drawer], the [Scaffold] will fill this widget with an - /// [IconButton] that opens the drawer. If there's no [Drawer] and the parent - /// [Navigator] can go back, the [NewGradientAppBar] will use a [BackButton] that calls - /// [Navigator.maybePop]. - final Widget? leading; - - /// Controls whether we should try to imply the leading widget if null. - /// - /// If true and [leading] is null, automatically try to deduce what the leading - /// widget should be. If false and [leading] is null, leading space is given to [title]. - /// If leading widget is not null, this parameter has no effect. - final bool automaticallyImplyLeading; - - /// The primary widget displayed in the appbar. - /// - /// Typically a [Text] widget containing a description of the current contents - /// of the app. - final Widget? title; - - /// Widgets to display after the [title] widget. - /// - /// Typically these widgets are [IconButton]s representing common operations. - /// For less common operations, consider using a [PopupMenuButton] as the - /// last action. - /// - /// {@tool sample} - /// - /// ```dart - /// Scaffold( - /// body: CustomScrollView( - /// primary: true, - /// slivers: [ - /// SliverGradientAppBar( - /// title: Text('Hello World'), - /// actions: [ - /// IconButton( - /// icon: Icon(Icons.shopping_cart), - /// tooltip: 'Open shopping cart', - /// onPressed: () { - /// // handle the press - /// }, - /// ), - /// ], - /// ), - /// // ...rest of body... - /// ], - /// ), - /// ) - /// ``` - /// {@end-tool} - final List? actions; - - /// This widget is stacked behind the toolbar and the tab bar. It's height will - /// be the same as the app bar's overall height. - /// - /// Typically a [FlexibleSpaceBar]. See [FlexibleSpaceBar] for details. - final Widget? flexibleSpace; - - /// This widget appears across the bottom of the appbar. - /// - /// Typically a [TabBar]. Only widgets that implement [PreferredSizeWidget] can - /// be used at the bottom of an app bar. - /// - /// See also: - /// - /// * [PreferredSize], which can be used to give an arbitrary widget a preferred size. - final PreferredSizeWidget? bottom; - - /// The z-coordinate at which to place this app bar when it is above other - /// content. This controls the size of the shadow below the app bar. - /// - /// If this property is null, then [ThemeData.appBarTheme.elevation] is used, - /// if that is also null, the default value is 4, the appropriate elevation - /// for app bars. - /// - /// If [forceElevated] is false, the elevation is ignored when the app bar has - /// no content underneath it. For example, if the app bar is [pinned] but no - /// content is scrolled under it, or if it scrolls with the content, then no - /// shadow is drawn, regardless of the value of [elevation]. - final double? elevation; - - /// Whether to show the shadow appropriate for the [elevation] even if the - /// content is not scrolled under the [NewGradientAppBar]. - /// - /// Defaults to false, meaning that the [elevation] is only applied when the - /// [NewGradientAppBar] is being displayed over content that is scrolled under it. - /// - /// When set to true, the [elevation] is applied regardless. - /// - /// Ignored when [elevation] is zero. - final bool forceElevated; - /// The gradient displayed at the appbar. - /// - /// If this property is null, then [ThemeData.appBarTheme.color] is used, - /// if that is also null, then [ThemeData.primaryColor] is used. - final Gradient? gradient; - - /// The brightness of the app bar's material. Typically this is set along - /// with [backgroundColor], [iconTheme], [textTheme]. - /// - /// If this property is null, then [ThemeData.appBarTheme.brightness] is used, - /// if that is also null, then [ThemeData.primaryColorBrightness] is used. - final Brightness? brightness; - - /// The color, opacity, and size to use for app bar icons. Typically this - /// is set along with [backgroundColor], [brightness], [textTheme]. - /// - /// If this property is null, then [ThemeData.appBarTheme.iconTheme] is used, - /// if that is also null, then [ThemeData.primaryIconTheme] is used. - final IconThemeData? iconTheme; - - /// The color, opacity, and size to use for trailing app bar icons. This - /// should only be used when the trailing icons should be themed differently - /// than the leading icons. - /// - /// If this property is null, then [ThemeData.appBarTheme.actionsIconTheme] is - /// used, if that is also null, then this falls back to [iconTheme]. - final IconThemeData? actionsIconTheme; - - /// The typographic styles to use for text in the app bar. Typically this is - /// set along with [brightness] [backgroundColor], [iconTheme]. - /// - /// If this property is null, then [ThemeData.appBarTheme.textTheme] is used, - /// if that is also null, then [ThemeData.primaryTextTheme] is used. - final TextTheme? textTheme; - - /// Whether this app bar is being displayed at the top of the screen. - /// - /// If this is true, the top padding specified by the [MediaQuery] will be - /// added to the top of the toolbar. - final bool primary; - - /// Whether the title should be centered. - /// - /// Defaults to being adapted to the current [TargetPlatform]. - final bool? centerTitle; - - /// The spacing around [title] content on the horizontal axis. This spacing is - /// applied even if there is no [leading] content or [actions]. If you want - /// [title] to take all the space available, set this value to 0.0. - /// - /// Defaults to [NavigationToolbar.kMiddleSpacing]. - final double titleSpacing; - - /// The size of the app bar when it is fully expanded. - /// - /// By default, the total height of the toolbar and the bottom widget (if - /// any). If a [flexibleSpace] widget is specified this height should be big - /// enough to accommodate whatever that widget contains. - /// - /// This does not include the status bar height (which will be automatically - /// included if [primary] is true). - final double? expandedHeight; - - /// Whether the app bar should become visible as soon as the user scrolls - /// towards the app bar. - /// - /// Otherwise, the user will need to scroll near the top of the scroll view to - /// reveal the app bar. - /// - /// If [snap] is true then a scroll that exposes the app bar will trigger an - /// animation that slides the entire app bar into view. Similarly if a scroll - /// dismisses the app bar, the animation will slide it completely out of view. - /// - /// ## Animated Examples - /// - /// The following animations show how the app bar changes its scrolling - /// behavior based on the value of this property. - /// - /// * App bar with [floating] set to false: - /// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar.mp4} - /// * App bar with [floating] set to true: - /// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_floating.mp4} - /// - /// See also: - /// - /// * [SliverNewGradientAppBar] for more animated examples of how this property changes the - /// behavior of the app bar in combination with [pinned] and [snap]. - final bool floating; - - /// Whether the app bar should remain visible at the start of the scroll view. - /// - /// The app bar can still expand and contract as the user scrolls, but it will - /// remain visible rather than being scrolled out of view. - /// - /// ## Animated Examples - /// - /// The following animations show how the app bar changes its scrolling - /// behavior based on the value of this property. - /// - /// * App bar with [pinned] set to false: - /// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar.mp4} - /// * App bar with [pinned] set to true: - /// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_pinned.mp4} - /// - /// See also: - /// - /// * [SliverNewGradientAppBar] for more animated examples of how this property changes the - /// behavior of the app bar in combination with [floating]. - final bool pinned; - - /// The material's shape as well its shadow. - /// - /// A shadow is only displayed if the [elevation] is greater than - /// zero. - final ShapeBorder? shape; - - /// If [snap] and [floating] are true then the floating app bar will "snap" - /// into view. - /// - /// If [snap] is true then a scroll that exposes the floating app bar will - /// trigger an animation that slides the entire app bar into view. Similarly if - /// a scroll dismisses the app bar, the animation will slide the app bar - /// completely out of view. - /// - /// Snapping only applies when the app bar is floating, not when the appbar - /// appears at the top of its scroll view. - /// - /// ## Animated Examples - /// - /// The following animations show how the app bar changes its scrolling - /// behavior based on the value of this property. - /// - /// * App bar with [snap] set to false: - /// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_floating.mp4} - /// * App bar with [snap] set to true: - /// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_floating_snap.mp4} - /// - /// See also: - /// - /// * [SliverNewGradientAppBar] for more animated examples of how this property changes the - /// behavior of the app bar in combination with [pinned] and [floating]. - final bool snap; - - @override - _SliverNewGradientAppBarState createState() => - _SliverNewGradientAppBarState(); -} - -// This class is only Stateful because it owns the TickerProvider used -// by the floating appbar snap animation (via FloatingHeaderSnapConfiguration). -class _SliverNewGradientAppBarState extends State - with TickerProviderStateMixin { - FloatingHeaderSnapConfiguration? _snapConfiguration; - - void _updateSnapConfiguration() { - if (widget.snap && widget.floating) { - _snapConfiguration = FloatingHeaderSnapConfiguration( - curve: Curves.easeOut, - duration: const Duration(milliseconds: 200), - ); - } else { - _snapConfiguration = null; + List _buildActions( + TextStyle actionsTextStyle, IconThemeData actionsIconTheme) { + final List actionButtons = []; + for (final Widget action in widget.actions!) { + if (action is IconButton) { + actionButtons.add(action); + } else { + actionButtons.add(Padding( + padding: const EdgeInsets.only(right: 8.0), + child: IconTheme.merge( + data: actionsIconTheme, + child: DefaultTextStyle( + style: actionsTextStyle, + child: action, + ), + ), + )); + } } - } - - @override - void initState() { - super.initState(); - _updateSnapConfiguration(); - } - - @override - void didUpdateWidget(SliverNewGradientAppBar oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.snap != oldWidget.snap || widget.floating != oldWidget.floating) - _updateSnapConfiguration(); - } - - @override - Widget build(BuildContext context) { - assert(!widget.primary || debugCheckHasMediaQuery(context)); - final double topPadding = - widget.primary ? MediaQuery.of(context).padding.top : 0.0; - final double collapsedHeight = ((widget.pinned && widget.floating) - ? widget.bottom!.preferredSize.height + topPadding - : null)!; - - return MediaQuery.removePadding( - context: context, - removeBottom: true, - child: SliverPersistentHeader( - floating: widget.floating, - pinned: widget.pinned, - delegate: _SliverGradientAppBarDelegate( - leading: (widget.leading)!, - automaticallyImplyLeading: widget.automaticallyImplyLeading, - title: widget.title!, - actions: widget.actions!, - flexibleSpace: widget.flexibleSpace!, - bottom: widget.bottom!, - elevation: widget.elevation!, - forceElevated: widget.forceElevated, - gradient: widget.gradient!, - brightness: widget.brightness!, - iconTheme: widget.iconTheme!, - actionsIconTheme: widget.actionsIconTheme!, - textTheme: widget.textTheme!, - primary: widget.primary, - centerTitle: widget.centerTitle!, - titleSpacing: widget.titleSpacing, - expandedHeight: widget.expandedHeight!, - collapsedHeight: collapsedHeight, - topPadding: topPadding, - floating: widget.floating, - pinned: widget.pinned, - shape: widget.shape!, - snapConfiguration: (_snapConfiguration)!, - ), - ), - ); + return actionButtons; } } From a6002d255e47d9f5d8f047b08ae8ba962f70b9dd Mon Sep 17 00:00:00 2001 From: Baig Date: Thu, 18 May 2023 15:41:07 +0500 Subject: [PATCH 02/12] backwardsCompatibility is deprecated, updated the code to remove the usage of this property. --- lib/new_gradient_app_bar.dart | 185 +++++++++------------------------- 1 file changed, 47 insertions(+), 138 deletions(-) diff --git a/lib/new_gradient_app_bar.dart b/lib/new_gradient_app_bar.dart index f87f641..be8cf2b 100644 --- a/lib/new_gradient_app_bar.dart +++ b/lib/new_gradient_app_bar.dart @@ -1,129 +1,53 @@ -library new_gradient_app_bar; - -import 'dart:math' as math; - -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; - -const double _kLeadingWidth = kToolbarHeight; -class _ToolbarContainerLayout extends SingleChildLayoutDelegate { - const _ToolbarContainerLayout(); - - @override - BoxConstraints getConstraintsForChild(BoxConstraints constraints) { - return constraints.tighten(height: kToolbarHeight); - } - - @override - Size getSize(BoxConstraints constraints) { - return Size(constraints.maxWidth, kToolbarHeight); - } - - @override - Offset getPositionForChild(Size size, Size childSize) { - return Offset(0.0, size.height - childSize.height); - } - - @override - bool shouldRelayout(_ToolbarContainerLayout oldDelegate) => false; -} +class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { + final Widget? title; + final List? actions; + final Widget? leading; + final Widget? flexibleSpace; + final Widget? bottom; + final double? elevation; + final Color? backgroundColor; + final Brightness? brightness; + final double toolbarOpacity; + final double bottomOpacity; + final double titleSpacing; + final ShapeBorder? shape; + final IconThemeData? iconTheme; + final IconThemeData? actionsIconTheme; -class NewGradientAppBar extends StatefulWidget implements PreferredSizeWidget { - NewGradientAppBar({ + CustomAppBar({ Key? key, - this.leading, - this.automaticallyImplyLeading = true, this.title, this.actions, + this.leading, this.flexibleSpace, this.bottom, this.elevation, - this.shape, - this.gradient, + this.backgroundColor, this.brightness, - this.iconTheme, - this.actionsIconTheme, - this.primary = true, - this.centerTitle, - this.titleSpacing = NavigationToolbar.kMiddleSpacing, this.toolbarOpacity = 1.0, this.bottomOpacity = 1.0, - }) : assert(elevation == null || elevation >= 0.0), - preferredSize = Size.fromHeight(kToolbarHeight + (bottom?.preferredSize.height ?? 0.0)), - super(key: key); - - final Widget? leading; - final bool automaticallyImplyLeading; - final Widget? title; - final List? actions; - final Widget? flexibleSpace; - final PreferredSizeWidget? bottom; - final double? elevation; - final ShapeBorder? shape; - final Gradient? gradient; - final Brightness? brightness; - final IconThemeData? iconTheme; - final IconThemeData? actionsIconTheme; - final bool primary; - final bool? centerTitle; - final double titleSpacing; - final double toolbarOpacity; - final double bottomOpacity; - - @override - final Size preferredSize; - - bool? _getEffectiveCenterTitle(ThemeData themeData) { - if (centerTitle != null) return centerTitle; - assert(true); - switch (themeData.platform) { - case TargetPlatform.android: - case TargetPlatform.fuchsia: - return false; - case TargetPlatform.iOS: - return actions == null || actions!.length < 2; - case TargetPlatform.linux: - return false; - case TargetPlatform.macOS: - return false; - case TargetPlatform.windows: - return false; - } - } + this.titleSpacing = NavigationToolbar.kMiddleSpacing, + this.shape, + this.iconTheme, + this.actionsIconTheme, + }) : super(key: key); @override - _NewGradientAppBarState createState() => _NewGradientAppBarState(); -} - -class _NewGradientAppBarState extends State { - static const double _defaultElevation = 4.0; - - void _handleDrawerButton() { - Scaffold.of(context).openDrawer(); - } - - void _handleDrawerButtonEnd() { - Scaffold.of(context).openEndDrawer(); - } + Size get preferredSize => Size.fromHeight(kToolbarHeight); @override Widget build(BuildContext context) { - assert(!widget.primary || debugCheckHasMediaQuery(context)); - assert(debugCheckHasMaterialLocalizations(context)); final ThemeData themeData = Theme.of(context); final ModalRoute? parentRoute = ModalRoute.of(context); final bool hasDrawer = widget.automaticallyImplyLeading && parentRoute?.canPop == true; final bool useCloseButton = !widget.primary; final bool effectiveCenterTitle = widget._getEffectiveCenterTitle(themeData); - IconThemeData appBarIconTheme = - widget.iconTheme ?? themeData.primaryIconTheme; - IconThemeData actionsIconTheme = - widget.actionsIconTheme ?? themeData.primaryIconTheme; + IconThemeData appBarIconTheme = widget.iconTheme ?? themeData.primaryIconTheme; + IconThemeData actionsIconTheme = widget.actionsIconTheme ?? themeData.primaryIconTheme; TextStyle? appBarTextStyle = themeData.primaryTextTheme.headline6; TextStyle? actionsTextStyle = themeData.primaryTextTheme.subtitle1; @@ -143,26 +67,16 @@ class _NewGradientAppBarState extends State { ); } - final SystemUiOverlayStyle overlayStyle = - themeData.appBarTheme.systemOverlayStyle ?? - SystemUiOverlayStyle.light; - final bool backwardsCompatibility = themeData.appBarTheme - .backwardsCompatibility ??= - false; + final SystemUiOverlayStyle overlayStyle = themeData.appBarTheme.systemOverlayStyle ?? SystemUiOverlayStyle.light; return AnnotatedRegion( - value: backwardsCompatibility - ? overlayStyle.copyWith( - statusBarBrightness: widget.brightness, - ) - : overlayStyle, + value: overlayStyle.copyWith(statusBarBrightness: widget.brightness), child: Semantics( container: true, explicitChildNodes: true, child: Stack( children: [ - if (widget.flexibleSpace != null) - Positioned.fill(child: widget.flexibleSpace!), + if (widget.flexibleSpace != null) Positioned.fill(child: widget.flexibleSpace!), PositionedDirectional( top: 0.0, start: effectiveCenterTitle ? 0.0 : null, @@ -173,37 +87,28 @@ class _NewGradientAppBarState extends State { bottomOpacity: widget.bottomOpacity, elevation: widget.elevation ?? _defaultElevation, shape: widget.shape, - leading: widget.leading ?? - (hasDrawer ? null : (useCloseButton ? null : Container())), + leading: widget.leading ?? (hasDrawer ? null : (useCloseButton ? null : Container())), title: widget.title != null ? DefaultTextStyle( style: appBarTextStyle!, + softWrap: false, + overflow: TextOverflow.ellipsis, child: Semantics( - namesRoute: true, child: widget.title!, - header: true, + namesRoute: true, + container: true, ), ) : null, - actions: widget.actions != null - ? _buildActions(actionsTextStyle!, actionsIconTheme) - : null, + actions: _buildActions(context, appBarIconTheme, actionsIconTheme, actionsTextStyle), + bottom: widget.bottom, + brightness: widget.brightness, + iconTheme: appBarIconTheme, centerTitle: effectiveCenterTitle, titleSpacing: widget.titleSpacing, - toolbarOpacity: widget.toolbarOpacity, - bottomOpacity: widget.bottomOpacity, - backwardsCompatibility: backwardsCompatibility, - titleTextStyle: appBarTextStyle, - systemOverlayStyle: overlayStyle, + excludeHeaderSemantics: true, ), ), - if (widget.bottom != null) - Positioned( - left: 0.0, - right: 0.0, - bottom: 0.0, - child: widget.bottom!, - ), ], ), ), @@ -211,12 +116,16 @@ class _NewGradientAppBarState extends State { } List _buildActions( - TextStyle actionsTextStyle, IconThemeData actionsIconTheme) { + BuildContext context, + IconThemeData appBarIconTheme, + IconThemeData actionsIconTheme, + TextStyle actionsTextStyle, + ) { + if (widget.actions == null) return const []; + final List actionButtons = []; for (final Widget action in widget.actions!) { - if (action is IconButton) { - actionButtons.add(action); - } else { + if (action != null) { actionButtons.add(Padding( padding: const EdgeInsets.only(right: 8.0), child: IconTheme.merge( From 13d8a01fb9f899e911cf25ad73dc8d8981706b3f Mon Sep 17 00:00:00 2001 From: Baig Date: Thu, 18 May 2023 15:53:35 +0500 Subject: [PATCH 03/12] Update new_gradient_app_bar.dart for backwardCompatibilty --- lib/new_gradient_app_bar.dart | 183 +++++++++++++++++++++++++--------- 1 file changed, 136 insertions(+), 47 deletions(-) diff --git a/lib/new_gradient_app_bar.dart b/lib/new_gradient_app_bar.dart index be8cf2b..9b11a9f 100644 --- a/lib/new_gradient_app_bar.dart +++ b/lib/new_gradient_app_bar.dart @@ -1,53 +1,129 @@ +library new_gradient_app_bar; + +import 'dart:math' as math; + +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; -class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { - final Widget? title; - final List? actions; - final Widget? leading; - final Widget? flexibleSpace; - final Widget? bottom; - final double? elevation; - final Color? backgroundColor; - final Brightness? brightness; - final double toolbarOpacity; - final double bottomOpacity; - final double titleSpacing; - final ShapeBorder? shape; - final IconThemeData? iconTheme; - final IconThemeData? actionsIconTheme; +const double _kLeadingWidth = kToolbarHeight; + +class _ToolbarContainerLayout extends SingleChildLayoutDelegate { + const _ToolbarContainerLayout(); + + @override + BoxConstraints getConstraintsForChild(BoxConstraints constraints) { + return constraints.tighten(height: kToolbarHeight); + } - CustomAppBar({ + @override + Size getSize(BoxConstraints constraints) { + return Size(constraints.maxWidth, kToolbarHeight); + } + + @override + Offset getPositionForChild(Size size, Size childSize) { + return Offset(0.0, size.height - childSize.height); + } + + @override + bool shouldRelayout(_ToolbarContainerLayout oldDelegate) => false; +} + +class NewGradientAppBar extends StatefulWidget implements PreferredSizeWidget { + NewGradientAppBar({ Key? key, + this.leading, + this.automaticallyImplyLeading = true, this.title, this.actions, - this.leading, this.flexibleSpace, this.bottom, this.elevation, - this.backgroundColor, - this.brightness, - this.toolbarOpacity = 1.0, - this.bottomOpacity = 1.0, - this.titleSpacing = NavigationToolbar.kMiddleSpacing, this.shape, + this.gradient, + this.brightness, this.iconTheme, this.actionsIconTheme, - }) : super(key: key); + this.primary = true, + this.centerTitle, + this.titleSpacing = NavigationToolbar.kMiddleSpacing, + this.toolbarOpacity = 1.0, + this.bottomOpacity = 1.0, + }) : assert(elevation == null || elevation >= 0.0), + preferredSize = Size.fromHeight(kToolbarHeight + (bottom?.preferredSize.height ?? 0.0)), + super(key: key); + + final Widget? leading; + final bool automaticallyImplyLeading; + final Widget? title; + final List? actions; + final Widget? flexibleSpace; + final PreferredSizeWidget? bottom; + final double? elevation; + final ShapeBorder? shape; + final Gradient? gradient; + final Brightness? brightness; + final IconThemeData? iconTheme; + final IconThemeData? actionsIconTheme; + final bool primary; + final bool? centerTitle; + final double titleSpacing; + final double toolbarOpacity; + final double bottomOpacity; + + @override + final Size preferredSize; + + bool? _getEffectiveCenterTitle(ThemeData themeData) { + if (centerTitle != null) return centerTitle; + assert(true); + switch (themeData.platform) { + case TargetPlatform.android: + case TargetPlatform.fuchsia: + return false; + case TargetPlatform.iOS: + return actions == null || actions!.length < 2; + case TargetPlatform.linux: + return false; + case TargetPlatform.macOS: + return false; + case TargetPlatform.windows: + return false; + } + } @override - Size get preferredSize => Size.fromHeight(kToolbarHeight); + _NewGradientAppBarState createState() => _NewGradientAppBarState(); +} + +class _NewGradientAppBarState extends State { + static const double _defaultElevation = 4.0; + + void _handleDrawerButton() { + Scaffold.of(context).openDrawer(); + } + + void _handleDrawerButtonEnd() { + Scaffold.of(context).openEndDrawer(); + } @override Widget build(BuildContext context) { + assert(!widget.primary || debugCheckHasMediaQuery(context)); + assert(debugCheckHasMaterialLocalizations(context)); final ThemeData themeData = Theme.of(context); final ModalRoute? parentRoute = ModalRoute.of(context); final bool hasDrawer = widget.automaticallyImplyLeading && parentRoute?.canPop == true; final bool useCloseButton = !widget.primary; final bool effectiveCenterTitle = widget._getEffectiveCenterTitle(themeData); - IconThemeData appBarIconTheme = widget.iconTheme ?? themeData.primaryIconTheme; - IconThemeData actionsIconTheme = widget.actionsIconTheme ?? themeData.primaryIconTheme; + IconThemeData appBarIconTheme = + widget.iconTheme ?? themeData.primaryIconTheme; + IconThemeData actionsIconTheme = + widget.actionsIconTheme ?? themeData.primaryIconTheme; TextStyle? appBarTextStyle = themeData.primaryTextTheme.headline6; TextStyle? actionsTextStyle = themeData.primaryTextTheme.subtitle1; @@ -67,16 +143,24 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { ); } - final SystemUiOverlayStyle overlayStyle = themeData.appBarTheme.systemOverlayStyle ?? SystemUiOverlayStyle.light; + final SystemUiOverlayStyle overlayStyle = + themeData.appBarTheme.systemOverlayStyle ?? + SystemUiOverlayStyle.light; + final bool backwardsCompatibility = false; return AnnotatedRegion( - value: overlayStyle.copyWith(statusBarBrightness: widget.brightness), + value: backwardsCompatibility + ? overlayStyle.copyWith( + statusBarBrightness: widget.brightness, + ) + : overlayStyle, child: Semantics( container: true, explicitChildNodes: true, child: Stack( children: [ - if (widget.flexibleSpace != null) Positioned.fill(child: widget.flexibleSpace!), + if (widget.flexibleSpace != null) + Positioned.fill(child: widget.flexibleSpace!), PositionedDirectional( top: 0.0, start: effectiveCenterTitle ? 0.0 : null, @@ -87,28 +171,37 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { bottomOpacity: widget.bottomOpacity, elevation: widget.elevation ?? _defaultElevation, shape: widget.shape, - leading: widget.leading ?? (hasDrawer ? null : (useCloseButton ? null : Container())), + leading: widget.leading ?? + (hasDrawer ? null : (useCloseButton ? null : Container())), title: widget.title != null ? DefaultTextStyle( style: appBarTextStyle!, - softWrap: false, - overflow: TextOverflow.ellipsis, child: Semantics( - child: widget.title!, namesRoute: true, - container: true, + child: widget.title!, + header: true, ), ) : null, - actions: _buildActions(context, appBarIconTheme, actionsIconTheme, actionsTextStyle), - bottom: widget.bottom, - brightness: widget.brightness, - iconTheme: appBarIconTheme, + actions: widget.actions != null + ? _buildActions(actionsTextStyle!, actionsIconTheme) + : null, centerTitle: effectiveCenterTitle, titleSpacing: widget.titleSpacing, - excludeHeaderSemantics: true, + toolbarOpacity: widget.toolbarOpacity, + bottomOpacity: widget.bottomOpacity, + backwardsCompatibility: backwardsCompatibility, + titleTextStyle: appBarTextStyle, + systemOverlayStyle: overlayStyle, ), ), + if (widget.bottom != null) + Positioned( + left: 0.0, + right: 0.0, + bottom: 0.0, + child: widget.bottom!, + ), ], ), ), @@ -116,16 +209,12 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { } List _buildActions( - BuildContext context, - IconThemeData appBarIconTheme, - IconThemeData actionsIconTheme, - TextStyle actionsTextStyle, - ) { - if (widget.actions == null) return const []; - + TextStyle actionsTextStyle, IconThemeData actionsIconTheme) { final List actionButtons = []; for (final Widget action in widget.actions!) { - if (action != null) { + if (action is IconButton) { + actionButtons.add(action); + } else { actionButtons.add(Padding( padding: const EdgeInsets.only(right: 8.0), child: IconTheme.merge( From 06bf5ef0cdd19bb74b141a51a4e8c319537f4cbd Mon Sep 17 00:00:00 2001 From: Baig Date: Thu, 18 May 2023 16:14:36 +0500 Subject: [PATCH 04/12] Update new_gradient_app_bar.dart --- lib/new_gradient_app_bar.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/new_gradient_app_bar.dart b/lib/new_gradient_app_bar.dart index 9b11a9f..6be5dd7 100644 --- a/lib/new_gradient_app_bar.dart +++ b/lib/new_gradient_app_bar.dart @@ -190,7 +190,6 @@ class _NewGradientAppBarState extends State { titleSpacing: widget.titleSpacing, toolbarOpacity: widget.toolbarOpacity, bottomOpacity: widget.bottomOpacity, - backwardsCompatibility: backwardsCompatibility, titleTextStyle: appBarTextStyle, systemOverlayStyle: overlayStyle, ), From 2eea75ca110d039ec023c6a5289b3db1d05d1501 Mon Sep 17 00:00:00 2001 From: Baig Date: Thu, 18 May 2023 16:27:46 +0500 Subject: [PATCH 05/12] Update new_gradient_app_bar.dart --- lib/new_gradient_app_bar.dart | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/new_gradient_app_bar.dart b/lib/new_gradient_app_bar.dart index 6be5dd7..eedb35b 100644 --- a/lib/new_gradient_app_bar.dart +++ b/lib/new_gradient_app_bar.dart @@ -53,7 +53,8 @@ class NewGradientAppBar extends StatefulWidget implements PreferredSizeWidget { this.toolbarOpacity = 1.0, this.bottomOpacity = 1.0, }) : assert(elevation == null || elevation >= 0.0), - preferredSize = Size.fromHeight(kToolbarHeight + (bottom?.preferredSize.height ?? 0.0)), + preferredSize = Size.fromHeight( + kToolbarHeight + (bottom?.preferredSize.height ?? 0.0)), super(key: key); final Widget? leading; @@ -77,8 +78,8 @@ class NewGradientAppBar extends StatefulWidget implements PreferredSizeWidget { @override final Size preferredSize; - bool? _getEffectiveCenterTitle(ThemeData themeData) { - if (centerTitle != null) return centerTitle; + bool _getEffectiveCenterTitle(ThemeData themeData) { + if (centerTitle != null) return centerTitle!; assert(true); switch (themeData.platform) { case TargetPlatform.android: @@ -116,9 +117,11 @@ class _NewGradientAppBarState extends State { assert(debugCheckHasMaterialLocalizations(context)); final ThemeData themeData = Theme.of(context); final ModalRoute? parentRoute = ModalRoute.of(context); - final bool hasDrawer = widget.automaticallyImplyLeading && parentRoute?.canPop == true; + final bool hasDrawer = + widget.automaticallyImplyLeading && parentRoute?.canPop == true; final bool useCloseButton = !widget.primary; - final bool effectiveCenterTitle = widget._getEffectiveCenterTitle(themeData); + final bool effectiveCenterTitle = + widget._getEffectiveCenterTitle(themeData); IconThemeData appBarIconTheme = widget.iconTheme ?? themeData.primaryIconTheme; @@ -144,8 +147,7 @@ class _NewGradientAppBarState extends State { } final SystemUiOverlayStyle overlayStyle = - themeData.appBarTheme.systemOverlayStyle ?? - SystemUiOverlayStyle.light; + themeData.appBarTheme.systemOverlayStyle ?? SystemUiOverlayStyle.light; final bool backwardsCompatibility = false; return AnnotatedRegion( From 18c21135e90cafba106c1cfffcd606c72005f000 Mon Sep 17 00:00:00 2001 From: Baig Date: Thu, 18 May 2023 16:35:41 +0500 Subject: [PATCH 06/12] Update new_gradient_app_bar.dart --- lib/new_gradient_app_bar.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/new_gradient_app_bar.dart b/lib/new_gradient_app_bar.dart index eedb35b..3c88f06 100644 --- a/lib/new_gradient_app_bar.dart +++ b/lib/new_gradient_app_bar.dart @@ -169,7 +169,6 @@ class _NewGradientAppBarState extends State { end: effectiveCenterTitle ? 0.0 : null, child: AppBar( backgroundColor: Colors.transparent, - toolbarOpacity: widget.toolbarOpacity, bottomOpacity: widget.bottomOpacity, elevation: widget.elevation ?? _defaultElevation, shape: widget.shape, From 8c8ee144ef56169286e9b43e36bcede46b1d1a98 Mon Sep 17 00:00:00 2001 From: Baig Date: Thu, 18 May 2023 16:47:00 +0500 Subject: [PATCH 07/12] Update new_gradient_app_bar.dart --- lib/new_gradient_app_bar.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/new_gradient_app_bar.dart b/lib/new_gradient_app_bar.dart index 3c88f06..ee9229e 100644 --- a/lib/new_gradient_app_bar.dart +++ b/lib/new_gradient_app_bar.dart @@ -190,7 +190,6 @@ class _NewGradientAppBarState extends State { centerTitle: effectiveCenterTitle, titleSpacing: widget.titleSpacing, toolbarOpacity: widget.toolbarOpacity, - bottomOpacity: widget.bottomOpacity, titleTextStyle: appBarTextStyle, systemOverlayStyle: overlayStyle, ), From 3b1a396d68b0e3abff3db0c8b1c7334512a328bc Mon Sep 17 00:00:00 2001 From: Baig Date: Fri, 19 May 2023 16:28:13 +0500 Subject: [PATCH 08/12] Delete pubspec.lock --- pubspec.lock | 146 --------------------------------------------------- 1 file changed, 146 deletions(-) delete mode 100644 pubspec.lock diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 49a0ce6..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,146 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - url: "https://pub.dartlang.org" - source: hosted - version: "2.5.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.dartlang.org" - source: hosted - version: "1.15.0" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.10" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" - path: - dependency: transitive - description: - name: path - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.19" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" -sdks: - dart: ">=2.12.0 <3.0.0" From 4fee558b7d2396665f128c28940d0c4d1fd25005 Mon Sep 17 00:00:00 2001 From: Baig Date: Fri, 19 May 2023 16:28:37 +0500 Subject: [PATCH 09/12] Delete pubspec.lock --- example/pubspec.lock | 160 ------------------------------------------- 1 file changed, 160 deletions(-) delete mode 100644 example/pubspec.lock diff --git a/example/pubspec.lock b/example/pubspec.lock deleted file mode 100644 index 2a949b8..0000000 --- a/example/pubspec.lock +++ /dev/null @@ -1,160 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - url: "https://pub.dartlang.org" - source: hosted - version: "2.5.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.dartlang.org" - source: hosted - version: "1.15.0" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.2" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.10" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" - new_gradient_app_bar: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "0.2.0" - path: - dependency: transitive - description: - name: path - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.19" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" -sdks: - dart: ">=2.12.0 <3.0.0" From 2e8c8c0b63640c748a93d5ab00475cb26d4c93ee Mon Sep 17 00:00:00 2001 From: Baig Date: Fri, 19 May 2023 16:30:14 +0500 Subject: [PATCH 10/12] Update new_gradient_app_bar.dart using dart fix --- lib/new_gradient_app_bar.dart | 1204 ++++++++++++++++++++++++++++++--- 1 file changed, 1103 insertions(+), 101 deletions(-) diff --git a/lib/new_gradient_app_bar.dart b/lib/new_gradient_app_bar.dart index ee9229e..26f324a 100644 --- a/lib/new_gradient_app_bar.dart +++ b/lib/new_gradient_app_bar.dart @@ -6,10 +6,11 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; -const double _kLeadingWidth = kToolbarHeight; +const double _kLeadingWidth = + kToolbarHeight; // So the leading button is square. +// Bottom justify the kToolbarHeight child which may overflow the top. class _ToolbarContainerLayout extends SingleChildLayoutDelegate { const _ToolbarContainerLayout(); @@ -32,7 +33,134 @@ class _ToolbarContainerLayout extends SingleChildLayoutDelegate { bool shouldRelayout(_ToolbarContainerLayout oldDelegate) => false; } +// TODO(eseidel): Toolbar needs to change size based on orientation: +// https://material.io/design/components/app-bars-top.html#specs +// Mobile Landscape: 48dp +// Mobile Portrait: 56dp +// Tablet/Desktop: 64dp + +/// A material design app bar. +/// +/// An app bar consists of a toolbar and potentially other widgets, such as a +/// [TabBar] and a [FlexibleSpaceBar]. App bars typically expose one or more +/// common [actions] with [IconButton]s which are optionally followed by a +/// [PopupMenuButton] for less common operations (sometimes called the "overflow +/// menu"). +/// +/// App bars are typically used in the [Scaffold.appBar] property, which places +/// the app bar as a fixed-height widget at the top of the screen. For a scrollable +/// app bar, see [SliverNewGradientAppBar], which embeds an [NewGradientAppBar] in a sliver for use in +/// a [CustomScrollView]. +/// +/// When not used as [Scaffold.appBar], or when wrapped in a [Hero], place the app +/// bar in a [MediaQuery] to take care of the padding around the content of the +/// app bar if needed, as the padding will not be handled by [Scaffold]. +/// +/// The GradientAppBar displays the toolbar widgets, [leading], [title], and [actions], +/// above the [bottom] (if any). The [bottom] is usually used for a [TabBar]. If +/// a [flexibleSpace] widget is specified then it is stacked behind the toolbar +/// and the bottom widget. The following diagram shows where each of these slots +/// appears in the toolbar when the writing language is left-to-right (e.g. +/// English): +/// +/// ![The leading widget is in the top left, the actions are in the top right, +/// the title is between them. The bottom is, naturally, at the bottom, and the +/// flexibleSpace is behind all of them.](https://flutter.github.io/assets-for-api-docs/assets/material/app_bar.png) +/// +/// If the [leading] widget is omitted, but the [NewGradientAppBar] is in a [Scaffold] with +/// a [Drawer], then a button will be inserted to open the drawer. Otherwise, if +/// the nearest [Navigator] has any previous routes, a [BackButton] is inserted +/// instead. This behavior can be turned off by setting the [automaticallyImplyLeading] +/// to false. In that case a null leading widget will result in the middle/title widget +/// stretching to start. +/// +/// {@tool snippet --template=stateless_widget_material} +/// +/// This sample shows an [NewGradientAppBar] with two simple actions. The first action +/// opens a [SnackBar], while the second action navigates to a new page. +/// +/// ```dart preamble +/// final GlobalKey scaffoldKey = GlobalKey(); +/// final SnackBar snackBar = const SnackBar(content: Text('Showing Snackbar')); +/// +/// void openPage(BuildContext context) { +/// Navigator.push(context, MaterialPageRoute( +/// builder: (BuildContext context) { +/// return Scaffold( +/// appBar: GradientAppBar( +/// title: const Text('Next page'), +/// ), +/// body: const Center( +/// child: Text( +/// 'This is the next page', +/// style: TextStyle(fontSize: 24), +/// ), +/// ), +/// ); +/// }, +/// )); +/// } +/// ``` +/// +/// ```dart +/// Widget build(BuildContext context) { +/// return Scaffold( +/// key: scaffoldKey, +/// appBar: GradientAppBar( +/// title: const Text('GradientAppBar Demo'), +/// actions: [ +/// IconButton( +/// icon: const Icon(Icons.add_alert), +/// tooltip: 'Show Snackbar', +/// onPressed: () { +/// scaffoldKey.currentState.showSnackBar(snackBar); +/// }, +/// ), +/// IconButton( +/// icon: const Icon(Icons.navigate_next), +/// tooltip: 'Next page', +/// onPressed: () { +/// openPage(context); +/// }, +/// ), +/// ], +/// ), +/// body: const Center( +/// child: Text( +/// 'This is the home page', +/// style: TextStyle(fontSize: 24), +/// ), +/// ), +/// ); +/// } +/// ``` +/// {@end-tool} +/// +/// See also: +/// +/// * [Scaffold], which displays the [NewGradientAppBar] in its [Scaffold.appBar] slot. +/// * [SliverNewGradientAppBar], which uses [NewGradientAppBar] to provide a flexible app bar that +/// can be used in a [CustomScrollView]. +/// * [TabBar], which is typically placed in the [bottom] slot of the [NewGradientAppBar] +/// if the screen has multiple pages arranged in tabs. +/// * [IconButton], which is used with [actions] to show buttons on the app bar. +/// * [PopupMenuButton], to show a popup menu on the app bar, via [actions]. +/// * [FlexibleSpaceBar], which is used with [flexibleSpace] when the app bar +/// can expand and collapse. +/// * class NewGradientAppBar extends StatefulWidget implements PreferredSizeWidget { + /// Creates a material design app bar. + /// + /// The arguments [primary], [toolbarOpacity], [bottomOpacity] + /// and [automaticallyImplyLeading] must not be null. Additionally, if + /// [elevation] is specified, it must be non-negative. + /// + /// If [elevation], [brightness], [iconTheme], + /// [actionsIconTheme], or [textTheme] are null, then their [GradientAppBarTheme] + /// values will be used. If the corresponding [GradientAppBarTheme] property is null, + /// then the default specified in the property's documentation will be used. + /// + /// Typically used in the [Scaffold.appBar] property. NewGradientAppBar({ Key? key, this.leading, @@ -47,6 +175,7 @@ class NewGradientAppBar extends StatefulWidget implements PreferredSizeWidget { this.brightness, this.iconTheme, this.actionsIconTheme, + this.textTheme, this.primary = true, this.centerTitle, this.titleSpacing = NavigationToolbar.kMiddleSpacing, @@ -57,29 +186,186 @@ class NewGradientAppBar extends StatefulWidget implements PreferredSizeWidget { kToolbarHeight + (bottom?.preferredSize.height ?? 0.0)), super(key: key); + /// A widget to display before the [title]. + /// + /// If this is null and [automaticallyImplyLeading] is set to true, the + /// [NewGradientAppBar] will imply an appropriate widget. For example, if the [NewGradientAppBar] is + /// in a [Scaffold] that also has a [Drawer], the [Scaffold] will fill this + /// widget with an [IconButton] that opens the drawer (using [Icons.menu]). If + /// there's no [Drawer] and the parent [Navigator] can go back, the [NewGradientAppBar] + /// will use a [BackButton] that calls [Navigator.maybePop]. + /// + /// {@tool sample} + /// + /// The following code shows how the drawer button could be manually specified + /// instead of relying on [automaticallyImplyLeading]: + /// + /// ```dart + /// GradientAppBar( + /// leading: Builder( + /// builder: (BuildContext context) { + /// return IconButton( + /// icon: const Icon(Icons.menu), + /// onPressed: () { Scaffold.of(context).openDrawer(); }, + /// tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip, + /// ); + /// }, + /// ), + /// ) + /// ``` + /// {@end-tool} + /// + /// The [Builder] is used in this example to ensure that the `context` refers + /// to that part of the subtree. That way this code snippet can be used even + /// inside the very code that is creating the [Scaffold] (in which case, + /// without the [Builder], the `context` wouldn't be able to see the + /// [Scaffold], since it would refer to an ancestor of that widget). + /// + /// See also: + /// + /// * [Scaffold.appBar], in which an [NewGradientAppBar] is usually placed. + /// * [Scaffold.drawer], in which the [Drawer] is usually placed. final Widget? leading; + + /// Controls whether we should try to imply the leading widget if null. + /// + /// If true and [leading] is null, automatically try to deduce what the leading + /// widget should be. If false and [leading] is null, leading space is given to [title]. + /// If leading widget is not null, this parameter has no effect. final bool automaticallyImplyLeading; + + /// The primary widget displayed in the appbar. + /// + /// Typically a [Text] widget containing a description of the current contents + /// of the app. final Widget? title; + + /// Widgets to display after the [title] widget. + /// + /// Typically these widgets are [IconButton]s representing common operations. + /// For less common operations, consider using a [PopupMenuButton] as the + /// last action. final List? actions; + + /// This widget is stacked behind the toolbar and the tab bar. It's height will + /// be the same as the app bar's overall height. + /// + /// A flexible space isn't actually flexible unless the [NewGradientAppBar]'s container + /// changes the [NewGradientAppBar]'s size. A [SliverNewGradientAppBar] in a [CustomScrollView] + /// changes the [NewGradientAppBar]'s height when scrolled. + /// + /// Typically a [FlexibleSpaceBar]. See [FlexibleSpaceBar] for details. final Widget? flexibleSpace; + + /// This widget appears across the bottom of the app bar. + /// + /// Typically a [TabBar]. Only widgets that implement [PreferredSizeWidget] can + /// be used at the bottom of an app bar. + /// + /// See also: + /// + /// * [PreferredSize], which can be used to give an arbitrary widget a preferred size. final PreferredSizeWidget? bottom; + + /// The z-coordinate at which to place this app bar relative to its parent. + /// + /// This controls the size of the shadow below the app bar. + /// + /// The value is non-negative. + /// + /// If this property is null, then [ThemeData.appBarTheme.elevation] is used, + /// if that is also null, the default value is 4, the appropriate elevation + /// for app bars. final double? elevation; + + /// The material's shape as well its shadow. + /// + /// A shadow is only displayed if the [elevation] is greater than + /// zero. final ShapeBorder? shape; + + /// The color to use for the app bar's material. Typically this should be set + /// along with [brightness], [iconTheme], [textTheme]. + /// + /// If this property is null, then [ThemeData.appBarTheme.color] is used, + /// if that is also null, then [ThemeData.primaryColor] is used. final Gradient? gradient; + + /// The gradient displayed at the appbar. + /// + /// If this property is null, then [ThemeData.appBarTheme.brightness] is used, + /// if that is also null, then [ThemeData.primaryColorBrightness] is used. final Brightness? brightness; + + /// The color, opacity, and size to use for app bar icons. Typically this + /// is set along with [backgroundColor], [brightness], [textTheme]. + /// + /// If this property is null, then [ThemeData.appBarTheme.iconTheme] is used, + /// if that is also null, then [ThemeData.primaryIconTheme] is used. final IconThemeData? iconTheme; + + /// The color, opacity, and size to use for the icons that appear in the app + /// bar's [actions]. This should only be used when the [actions] should be + /// themed differently than the icon that appears in the app bar's [leading] + /// widget. + /// + /// If this property is null, then [ThemeData.appBarTheme.actionsIconTheme] is + /// used, if that is also null, then this falls back to [iconTheme]. final IconThemeData? actionsIconTheme; + + /// The typographic styles to use for text in the app bar. Typically this is + /// set along with [brightness] [backgroundColor], [iconTheme]. + /// + /// If this property is null, then [ThemeData.appBarTheme.textTheme] is used, + /// if that is also null, then [ThemeData.primaryTextTheme] is used. + final TextTheme? textTheme; + + /// Whether this app bar is being displayed at the top of the screen. + /// + /// If true, the app bar's toolbar elements and [bottom] widget will be + /// padded on top by the height of the system status bar. The layout + /// of the [flexibleSpace] is not affected by the [primary] property. final bool primary; + + /// Whether the title should be centered. + /// + /// Defaults to being adapted to the current [TargetPlatform]. final bool? centerTitle; + + /// The spacing around [title] content on the horizontal axis. This spacing is + /// applied even if there is no [leading] content or [actions]. If you want + /// [title] to take all the space available, set this value to 0.0. + /// + /// Defaults to [NavigationToolbar.kMiddleSpacing]. final double titleSpacing; + + /// How opaque the toolbar part of the app bar is. + /// + /// A value of 1.0 is fully opaque, and a value of 0.0 is fully transparent. + /// + /// Typically, this value is not changed from its default value (1.0). It is + /// used by [SliverNewGradientAppBar] to animate the opacity of the toolbar when the app + /// bar is scrolled. final double toolbarOpacity; + + /// How opaque the bottom part of the app bar is. + /// + /// A value of 1.0 is fully opaque, and a value of 0.0 is fully transparent. + /// + /// Typically, this value is not changed from its default value (1.0). It is + /// used by [SliverNewGradientAppBar] to animate the opacity of the toolbar when the app + /// bar is scrolled. final double bottomOpacity; + /// A size whose height is the sum of [kToolbarHeight] and the [bottom] widget's + /// preferred height. + /// + /// [Scaffold] uses this this size to set its app bar's height. @override final Size preferredSize; - bool _getEffectiveCenterTitle(ThemeData themeData) { - if (centerTitle != null) return centerTitle!; + bool? _getEffectiveCenterTitle(ThemeData themeData) { + if (centerTitle != null) return centerTitle; assert(true); switch (themeData.platform) { case TargetPlatform.android: @@ -116,116 +402,832 @@ class _NewGradientAppBarState extends State { assert(!widget.primary || debugCheckHasMediaQuery(context)); assert(debugCheckHasMaterialLocalizations(context)); final ThemeData themeData = Theme.of(context); + final AppBarTheme appBarTheme = AppBarTheme.of(context); + final ScaffoldState scaffold = Scaffold.of(context); final ModalRoute? parentRoute = ModalRoute.of(context); - final bool hasDrawer = - widget.automaticallyImplyLeading && parentRoute?.canPop == true; - final bool useCloseButton = !widget.primary; - final bool effectiveCenterTitle = - widget._getEffectiveCenterTitle(themeData); - - IconThemeData appBarIconTheme = - widget.iconTheme ?? themeData.primaryIconTheme; - IconThemeData actionsIconTheme = - widget.actionsIconTheme ?? themeData.primaryIconTheme; - TextStyle? appBarTextStyle = themeData.primaryTextTheme.headline6; - TextStyle? actionsTextStyle = themeData.primaryTextTheme.subtitle1; - - if (widget.brightness != null) { - final bool isDark = widget.brightness == Brightness.dark; - appBarIconTheme = appBarIconTheme.copyWith( - opacity: isDark ? 0.7 : 1.0, - ); + + final bool hasDrawer = scaffold.hasDrawer; + final bool hasEndDrawer = scaffold.hasEndDrawer; + final bool canPop = parentRoute?.canPop ?? false; + final bool useCloseButton = + parentRoute is PageRoute && parentRoute.fullscreenDialog; + + IconThemeData overallIconTheme = + widget.iconTheme ?? appBarTheme.iconTheme ?? themeData.primaryIconTheme; + IconThemeData actionsIconTheme = widget.actionsIconTheme ?? + appBarTheme.actionsIconTheme ?? + overallIconTheme; + TextStyle centerStyle = (widget.textTheme?.titleLarge ?? + appBarTheme.titleTextStyle ?? + themeData.primaryTextTheme.titleLarge)!; + TextStyle? sideStyle = widget.textTheme?.bodyMedium ?? + appBarTheme.toolbarTextStyle ?? + themeData.primaryTextTheme.bodyMedium; + + if (widget.toolbarOpacity != 1.0) { + final double opacity = + const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn) + .transform(widget.toolbarOpacity); + if (centerStyle.color != null) + centerStyle = centerStyle.copyWith( + color: centerStyle.color!.withOpacity(opacity)); + if (sideStyle?.color != null) + sideStyle = + sideStyle!.copyWith(color: sideStyle.color!.withOpacity(opacity)); + overallIconTheme = overallIconTheme.copyWith( + opacity: opacity * (overallIconTheme.opacity ?? 1.0)); actionsIconTheme = actionsIconTheme.copyWith( - opacity: isDark ? 0.7 : 1.0, + opacity: opacity * (actionsIconTheme.opacity ?? 1.0)); + } + + Widget? leading = widget.leading; + if (leading == null && widget.automaticallyImplyLeading) { + if (hasDrawer) { + leading = IconButton( + icon: const Icon(Icons.menu), + onPressed: _handleDrawerButton, + tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip, + ); + } else { + if (canPop) + leading = useCloseButton ? const CloseButton() : const BackButton(); + } + } + if (leading != null) { + leading = ConstrainedBox( + constraints: const BoxConstraints.tightFor(width: _kLeadingWidth), + child: leading, + ); + } + + Widget? title = widget.title; + if (title != null) { + bool? namesRoute; + switch (defaultTargetPlatform) { + case TargetPlatform.android: + case TargetPlatform.fuchsia: + namesRoute = true; + break; + case TargetPlatform.iOS: + case TargetPlatform.linux: + case TargetPlatform.macOS: + case TargetPlatform.windows: + break; + } + title = DefaultTextStyle( + style: centerStyle, + softWrap: false, + overflow: TextOverflow.ellipsis, + child: Semantics( + namesRoute: namesRoute, + child: title, + header: true, + ), ); - appBarTextStyle = appBarTextStyle!.copyWith( - color: isDark ? Colors.white : Colors.black87, + } + + Widget? actions; + if (widget.actions != null && widget.actions!.isNotEmpty) { + actions = Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: (widget.actions)!, ); - actionsTextStyle = actionsTextStyle!.copyWith( - color: isDark ? Colors.white : Colors.black87, + } else if (hasEndDrawer) { + actions = IconButton( + icon: const Icon(Icons.menu), + onPressed: _handleDrawerButtonEnd, + tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip, ); } - final SystemUiOverlayStyle overlayStyle = - themeData.appBarTheme.systemOverlayStyle ?? SystemUiOverlayStyle.light; - final bool backwardsCompatibility = false; - - return AnnotatedRegion( - value: backwardsCompatibility - ? overlayStyle.copyWith( - statusBarBrightness: widget.brightness, - ) - : overlayStyle, - child: Semantics( - container: true, - explicitChildNodes: true, - child: Stack( - children: [ - if (widget.flexibleSpace != null) - Positioned.fill(child: widget.flexibleSpace!), - PositionedDirectional( - top: 0.0, - start: effectiveCenterTitle ? 0.0 : null, - end: effectiveCenterTitle ? 0.0 : null, - child: AppBar( - backgroundColor: Colors.transparent, - bottomOpacity: widget.bottomOpacity, - elevation: widget.elevation ?? _defaultElevation, - shape: widget.shape, - leading: widget.leading ?? - (hasDrawer ? null : (useCloseButton ? null : Container())), - title: widget.title != null - ? DefaultTextStyle( - style: appBarTextStyle!, - child: Semantics( - namesRoute: true, - child: widget.title!, - header: true, - ), - ) - : null, - actions: widget.actions != null - ? _buildActions(actionsTextStyle!, actionsIconTheme) - : null, - centerTitle: effectiveCenterTitle, - titleSpacing: widget.titleSpacing, - toolbarOpacity: widget.toolbarOpacity, - titleTextStyle: appBarTextStyle, - systemOverlayStyle: overlayStyle, - ), + // Allow the trailing actions to have their own theme if necessary. + if (actions != null) { + actions = IconTheme.merge( + data: actionsIconTheme, + child: actions, + ); + } + + final Widget toolbar = NavigationToolbar( + leading: leading, + middle: title, + trailing: actions, + centerMiddle: (widget._getEffectiveCenterTitle(themeData))!, + middleSpacing: widget.titleSpacing, + ); + + // If the toolbar is allocated less than kToolbarHeight make it + // appear to scroll upwards within its shrinking container. + Widget appBar = ClipRect( + child: CustomSingleChildLayout( + delegate: const _ToolbarContainerLayout(), + child: IconTheme.merge( + data: overallIconTheme, + child: DefaultTextStyle( + style: (sideStyle)!, + child: toolbar, + ), + ), + ), + ); + if (widget.bottom != null) { + appBar = Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: ConstrainedBox( + constraints: const BoxConstraints(maxHeight: kToolbarHeight), + child: appBar, ), - if (widget.bottom != null) - Positioned( - left: 0.0, - right: 0.0, - bottom: 0.0, - child: widget.bottom!, + ), + widget.bottomOpacity == 1.0 + ? widget.bottom! + : Opacity( + opacity: + const Interval(0.25, 1.0, curve: Curves.fastOutSlowIn) + .transform(widget.bottomOpacity), + child: widget.bottom!, + ), + ], + ); + } + + // The padding applies to the toolbar and tabbar, not the flexible space. + if (widget.primary) { + appBar = SafeArea( + top: true, + child: appBar, + ); + } + + appBar = Align( + alignment: Alignment.topCenter, + child: appBar, + ); + + if (widget.flexibleSpace != null) { + appBar = Stack( + fit: StackFit.passthrough, + children: [ + widget.flexibleSpace!, + appBar, + ], + ); + } + final Brightness brightness = widget.brightness ?? + appBarTheme.systemOverlayStyle?.statusBarBrightness ?? + themeData.brightness; + final SystemUiOverlayStyle overlayStyle = brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark; + + return Semantics( + container: true, + child: AnnotatedRegion( + value: overlayStyle, + child: Material( + color: appBarTheme.backgroundColor ?? themeData.primaryColor, + elevation: + widget.elevation ?? appBarTheme.elevation ?? _defaultElevation, + shape: widget.shape, + child: Container( + decoration: BoxDecoration(gradient: widget.gradient), + child: Semantics( + explicitChildNodes: true, + child: appBar, ), - ], - ), + )), ), ); } +} - List _buildActions( - TextStyle actionsTextStyle, IconThemeData actionsIconTheme) { - final List actionButtons = []; - for (final Widget action in widget.actions!) { - if (action is IconButton) { - actionButtons.add(action); - } else { - actionButtons.add(Padding( - padding: const EdgeInsets.only(right: 8.0), - child: IconTheme.merge( - data: actionsIconTheme, - child: DefaultTextStyle( - style: actionsTextStyle, - child: action, - ), - ), - )); - } +class _FloatingGradientAppBar extends StatefulWidget { + const _FloatingGradientAppBar({Key? key, this.child}) : super(key: key); + + final Widget? child; + + @override + _FloatingGradientAppBarState createState() => _FloatingGradientAppBarState(); +} + +// A wrapper for the widget created by _SliverGradientAppBarDelegate that starts and +// stops the floating app bar's snap-into-view or snap-out-of-view animation. +class _FloatingGradientAppBarState extends State<_FloatingGradientAppBar> { + ScrollPosition? _position; + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + if (_position != null) + _position!.isScrollingNotifier.removeListener(_isScrollingListener); + _position = Scrollable.of(context).position; + if (_position != null) + _position!.isScrollingNotifier.addListener(_isScrollingListener); + } + + @override + void dispose() { + if (_position != null) + _position!.isScrollingNotifier.removeListener(_isScrollingListener); + super.dispose(); + } + + RenderSliverFloatingPersistentHeader? _headerRenderer() { + return context + .findAncestorRenderObjectOfType(); + } + + void _isScrollingListener() { + // When a scroll stops, then maybe snap the appbar into view. + // Similarly, when a scroll starts, then maybe stop the snap animation. + final RenderSliverFloatingPersistentHeader? header = _headerRenderer(); + if (_position!.isScrollingNotifier.value) + header?.maybeStopSnapAnimation(_position!.userScrollDirection); + else + header?.maybeStartSnapAnimation(_position!.userScrollDirection); + } + + @override + Widget build(BuildContext context) => widget.child!; +} + +class _SliverGradientAppBarDelegate extends SliverPersistentHeaderDelegate { + _SliverGradientAppBarDelegate({ + required this.leading, + required this.automaticallyImplyLeading, + required this.title, + required this.actions, + required this.flexibleSpace, + required this.bottom, + required this.elevation, + required this.forceElevated, + required this.gradient, + required this.brightness, + required this.iconTheme, + required this.actionsIconTheme, + required this.textTheme, + required this.primary, + required this.centerTitle, + required this.titleSpacing, + required this.expandedHeight, + required this.collapsedHeight, + required this.topPadding, + required this.floating, + required this.pinned, + required this.snapConfiguration, + required this.shape, + }) : assert(primary || topPadding == 0.0), + _bottomHeight = bottom.preferredSize.height; + + final Widget leading; + final bool automaticallyImplyLeading; + final Widget title; + final List actions; + final Widget flexibleSpace; + final PreferredSizeWidget bottom; + final double elevation; + final bool forceElevated; + final Gradient gradient; + final Brightness brightness; + final IconThemeData iconTheme; + final IconThemeData actionsIconTheme; + final TextTheme textTheme; + final bool primary; + final bool centerTitle; + final double titleSpacing; + final double expandedHeight; + final double collapsedHeight; + final double topPadding; + final bool floating; + final bool pinned; + final ShapeBorder shape; + + final double _bottomHeight; + + @override + double get minExtent => collapsedHeight; + + @override + double get maxExtent => math.max(topPadding + expandedHeight, minExtent); + + @override + final FloatingHeaderSnapConfiguration snapConfiguration; + + @override + Widget build( + BuildContext context, double shrinkOffset, bool overlapsContent) { + final double visibleMainHeight = maxExtent - shrinkOffset - topPadding; + + // Truth table for `toolbarOpacity`: + // pinned | floating | bottom != null || opacity + // ---------------------------------------------- + // 0 | 0 | 0 || fade + // 0 | 0 | 1 || fade + // 0 | 1 | 0 || fade + // 0 | 1 | 1 || fade + // 1 | 0 | 0 || 1.0 + // 1 | 0 | 1 || 1.0 + // 1 | 1 | 0 || 1.0 + // 1 | 1 | 1 || fade + final double toolbarOpacity = !pinned || (floating) + ? ((visibleMainHeight - _bottomHeight) / kToolbarHeight).clamp(0.0, 1.0) + : 1.0; + + final Widget appBar = FlexibleSpaceBar.createSettings( + minExtent: minExtent, + maxExtent: maxExtent, + currentExtent: math.max(minExtent, maxExtent - shrinkOffset), + toolbarOpacity: toolbarOpacity, + child: NewGradientAppBar( + leading: leading, + automaticallyImplyLeading: automaticallyImplyLeading, + title: title, + actions: actions, + // ignore: unnecessary_null_comparison + flexibleSpace: (title == null && flexibleSpace != null) + ? Semantics(child: flexibleSpace, header: true) + : flexibleSpace, + bottom: bottom, + elevation: forceElevated || + overlapsContent || + (pinned && shrinkOffset > maxExtent - minExtent) + ? elevation + : 0.0, + gradient: gradient, + brightness: brightness, + iconTheme: iconTheme, + actionsIconTheme: actionsIconTheme, + textTheme: textTheme, + primary: primary, + centerTitle: centerTitle, + titleSpacing: titleSpacing, + shape: shape, + toolbarOpacity: toolbarOpacity, + bottomOpacity: + pinned ? 1.0 : (visibleMainHeight / _bottomHeight).clamp(0.0, 1.0), + ), + ); + return floating ? _FloatingGradientAppBar(child: appBar) : appBar; + } + + @override + bool shouldRebuild(covariant _SliverGradientAppBarDelegate oldDelegate) { + return leading != oldDelegate.leading || + automaticallyImplyLeading != oldDelegate.automaticallyImplyLeading || + title != oldDelegate.title || + actions != oldDelegate.actions || + flexibleSpace != oldDelegate.flexibleSpace || + bottom != oldDelegate.bottom || + _bottomHeight != oldDelegate._bottomHeight || + elevation != oldDelegate.elevation || + gradient != oldDelegate.gradient || + brightness != oldDelegate.brightness || + iconTheme != oldDelegate.iconTheme || + actionsIconTheme != oldDelegate.actionsIconTheme || + textTheme != oldDelegate.textTheme || + primary != oldDelegate.primary || + centerTitle != oldDelegate.centerTitle || + titleSpacing != oldDelegate.titleSpacing || + expandedHeight != oldDelegate.expandedHeight || + topPadding != oldDelegate.topPadding || + pinned != oldDelegate.pinned || + floating != oldDelegate.floating || + snapConfiguration != oldDelegate.snapConfiguration; + } + + @override + String toString() { + return '${describeIdentity(this)}(topPadding: ${topPadding.toStringAsFixed(1)}, bottomHeight: ${_bottomHeight.toStringAsFixed(1)}, ...)'; + } +} + +/// A material design app bar that integrates with a [CustomScrollView]. +/// +/// An app bar consists of a toolbar and potentially other widgets, such as a +/// [TabBar] and a [FlexibleSpaceBar]. App bars typically expose one or more +/// common actions with [IconButton]s which are optionally followed by a +/// [PopupMenuButton] for less common operations. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=R9C5KMJKluE} +/// +/// Sliver app bars are typically used as the first child of a +/// [CustomScrollView], which lets the app bar integrate with the scroll view so +/// that it can vary in height according to the scroll offset or float above the +/// other content in the scroll view. For a fixed-height app bar at the top of +/// the screen see [NewGradientAppBar], which is used in the [Scaffold.appBar] slot. +/// +/// The GradientAppBar displays the toolbar widgets, [leading], [title], and +/// [actions], above the [bottom] (if any). If a [flexibleSpace] widget is +/// specified then it is stacked behind the toolbar and the bottom widget. +/// +/// {@tool sample} +/// +/// This is an example that could be included in a [CustomScrollView]'s +/// [CustomScrollView.slivers] list: +/// +/// ```dart +/// SliverGradientAppBar( +/// expandedHeight: 150.0, +/// flexibleSpace: const FlexibleSpaceBar( +/// title: Text('Available seats'), +/// ), +/// actions: [ +/// IconButton( +/// icon: const Icon(Icons.add_circle), +/// tooltip: 'Add new entry', +/// onPressed: () { /* ... */ }, +/// ), +/// ] +/// ) +/// ``` +/// {@end-tool} +/// +/// ## Animated Examples +/// +/// The following animations show how app bars with different configurations +/// behave when a user scrolls up and then down again. +/// +/// * App bar with [floating]: false, [pinned]: false, [snap]: false: +/// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar.mp4} +/// +/// * App bar with [floating]: true, [pinned]: false, [snap]: false: +/// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_floating.mp4} +/// +/// * App bar with [floating]: true, [pinned]: false, [snap]: true: +/// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_floating_snap.mp4} +/// +/// * App bar with [floating]: true, [pinned]: true, [snap]: false: +/// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_pinned_floating.mp4} +/// +/// * App bar with [floating]: true, [pinned]: true, [snap]: true: +/// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_pinned_floating_snap.mp4} +/// +/// * App bar with [floating]: false, [pinned]: true, [snap]: false: +/// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_pinned.mp4} +/// +/// The property [snap] can only be set to true if [floating] is also true. +/// +/// See also: +/// +/// * [CustomScrollView], which integrates the [SliverNewGradientAppBar] into its +/// scrolling. +/// * [NewGradientAppBar], which is a fixed-height app bar for use in [Scaffold.appBar]. +/// * [TabBar], which is typically placed in the [bottom] slot of the [NewGradientAppBar] +/// if the screen has multiple pages arranged in tabs. +/// * [IconButton], which is used with [actions] to show buttons on the app bar. +/// * [PopupMenuButton], to show a popup menu on the app bar, via [actions]. +/// * [FlexibleSpaceBar], which is used with [flexibleSpace] when the app bar +/// can expand and collapse. +/// * +class SliverNewGradientAppBar extends StatefulWidget { + /// Creates a material design app bar that can be placed in a [CustomScrollView]. + /// + /// The arguments [forceElevated], [primary], [floating], [pinned], [snap] + /// and [automaticallyImplyLeading] must not be null. + const SliverNewGradientAppBar({ + Key? key, + this.leading, + this.automaticallyImplyLeading = true, + this.title, + this.actions, + this.flexibleSpace, + this.bottom, + this.elevation, + this.forceElevated = false, + this.gradient, + this.brightness, + this.iconTheme, + this.actionsIconTheme, + this.textTheme, + this.primary = true, + this.centerTitle, + this.titleSpacing = NavigationToolbar.kMiddleSpacing, + this.expandedHeight, + this.floating = false, + this.pinned = false, + this.snap = false, + this.shape, + }) : assert(floating || !snap, + 'The "snap" argument only makes sense for floating app bars.'), + super(key: key); + + /// A widget to display before the [title]. + /// + /// If this is null and [automaticallyImplyLeading] is set to true, the [NewGradientAppBar] will + /// imply an appropriate widget. For example, if the [NewGradientAppBar] is in a [Scaffold] + /// that also has a [Drawer], the [Scaffold] will fill this widget with an + /// [IconButton] that opens the drawer. If there's no [Drawer] and the parent + /// [Navigator] can go back, the [NewGradientAppBar] will use a [BackButton] that calls + /// [Navigator.maybePop]. + final Widget? leading; + + /// Controls whether we should try to imply the leading widget if null. + /// + /// If true and [leading] is null, automatically try to deduce what the leading + /// widget should be. If false and [leading] is null, leading space is given to [title]. + /// If leading widget is not null, this parameter has no effect. + final bool automaticallyImplyLeading; + + /// The primary widget displayed in the appbar. + /// + /// Typically a [Text] widget containing a description of the current contents + /// of the app. + final Widget? title; + + /// Widgets to display after the [title] widget. + /// + /// Typically these widgets are [IconButton]s representing common operations. + /// For less common operations, consider using a [PopupMenuButton] as the + /// last action. + /// + /// {@tool sample} + /// + /// ```dart + /// Scaffold( + /// body: CustomScrollView( + /// primary: true, + /// slivers: [ + /// SliverGradientAppBar( + /// title: Text('Hello World'), + /// actions: [ + /// IconButton( + /// icon: Icon(Icons.shopping_cart), + /// tooltip: 'Open shopping cart', + /// onPressed: () { + /// // handle the press + /// }, + /// ), + /// ], + /// ), + /// // ...rest of body... + /// ], + /// ), + /// ) + /// ``` + /// {@end-tool} + final List? actions; + + /// This widget is stacked behind the toolbar and the tab bar. It's height will + /// be the same as the app bar's overall height. + /// + /// Typically a [FlexibleSpaceBar]. See [FlexibleSpaceBar] for details. + final Widget? flexibleSpace; + + /// This widget appears across the bottom of the appbar. + /// + /// Typically a [TabBar]. Only widgets that implement [PreferredSizeWidget] can + /// be used at the bottom of an app bar. + /// + /// See also: + /// + /// * [PreferredSize], which can be used to give an arbitrary widget a preferred size. + final PreferredSizeWidget? bottom; + + /// The z-coordinate at which to place this app bar when it is above other + /// content. This controls the size of the shadow below the app bar. + /// + /// If this property is null, then [ThemeData.appBarTheme.elevation] is used, + /// if that is also null, the default value is 4, the appropriate elevation + /// for app bars. + /// + /// If [forceElevated] is false, the elevation is ignored when the app bar has + /// no content underneath it. For example, if the app bar is [pinned] but no + /// content is scrolled under it, or if it scrolls with the content, then no + /// shadow is drawn, regardless of the value of [elevation]. + final double? elevation; + + /// Whether to show the shadow appropriate for the [elevation] even if the + /// content is not scrolled under the [NewGradientAppBar]. + /// + /// Defaults to false, meaning that the [elevation] is only applied when the + /// [NewGradientAppBar] is being displayed over content that is scrolled under it. + /// + /// When set to true, the [elevation] is applied regardless. + /// + /// Ignored when [elevation] is zero. + final bool forceElevated; + + /// The gradient displayed at the appbar. + /// + /// If this property is null, then [ThemeData.appBarTheme.color] is used, + /// if that is also null, then [ThemeData.primaryColor] is used. + final Gradient? gradient; + + /// The brightness of the app bar's material. Typically this is set along + /// with [backgroundColor], [iconTheme], [textTheme]. + /// + /// If this property is null, then [ThemeData.appBarTheme.brightness] is used, + /// if that is also null, then [ThemeData.primaryColorBrightness] is used. + final Brightness? brightness; + + /// The color, opacity, and size to use for app bar icons. Typically this + /// is set along with [backgroundColor], [brightness], [textTheme]. + /// + /// If this property is null, then [ThemeData.appBarTheme.iconTheme] is used, + /// if that is also null, then [ThemeData.primaryIconTheme] is used. + final IconThemeData? iconTheme; + + /// The color, opacity, and size to use for trailing app bar icons. This + /// should only be used when the trailing icons should be themed differently + /// than the leading icons. + /// + /// If this property is null, then [ThemeData.appBarTheme.actionsIconTheme] is + /// used, if that is also null, then this falls back to [iconTheme]. + final IconThemeData? actionsIconTheme; + + /// The typographic styles to use for text in the app bar. Typically this is + /// set along with [brightness] [backgroundColor], [iconTheme]. + /// + /// If this property is null, then [ThemeData.appBarTheme.textTheme] is used, + /// if that is also null, then [ThemeData.primaryTextTheme] is used. + final TextTheme? textTheme; + + /// Whether this app bar is being displayed at the top of the screen. + /// + /// If this is true, the top padding specified by the [MediaQuery] will be + /// added to the top of the toolbar. + final bool primary; + + /// Whether the title should be centered. + /// + /// Defaults to being adapted to the current [TargetPlatform]. + final bool? centerTitle; + + /// The spacing around [title] content on the horizontal axis. This spacing is + /// applied even if there is no [leading] content or [actions]. If you want + /// [title] to take all the space available, set this value to 0.0. + /// + /// Defaults to [NavigationToolbar.kMiddleSpacing]. + final double titleSpacing; + + /// The size of the app bar when it is fully expanded. + /// + /// By default, the total height of the toolbar and the bottom widget (if + /// any). If a [flexibleSpace] widget is specified this height should be big + /// enough to accommodate whatever that widget contains. + /// + /// This does not include the status bar height (which will be automatically + /// included if [primary] is true). + final double? expandedHeight; + + /// Whether the app bar should become visible as soon as the user scrolls + /// towards the app bar. + /// + /// Otherwise, the user will need to scroll near the top of the scroll view to + /// reveal the app bar. + /// + /// If [snap] is true then a scroll that exposes the app bar will trigger an + /// animation that slides the entire app bar into view. Similarly if a scroll + /// dismisses the app bar, the animation will slide it completely out of view. + /// + /// ## Animated Examples + /// + /// The following animations show how the app bar changes its scrolling + /// behavior based on the value of this property. + /// + /// * App bar with [floating] set to false: + /// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar.mp4} + /// * App bar with [floating] set to true: + /// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_floating.mp4} + /// + /// See also: + /// + /// * [SliverNewGradientAppBar] for more animated examples of how this property changes the + /// behavior of the app bar in combination with [pinned] and [snap]. + final bool floating; + + /// Whether the app bar should remain visible at the start of the scroll view. + /// + /// The app bar can still expand and contract as the user scrolls, but it will + /// remain visible rather than being scrolled out of view. + /// + /// ## Animated Examples + /// + /// The following animations show how the app bar changes its scrolling + /// behavior based on the value of this property. + /// + /// * App bar with [pinned] set to false: + /// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar.mp4} + /// * App bar with [pinned] set to true: + /// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_pinned.mp4} + /// + /// See also: + /// + /// * [SliverNewGradientAppBar] for more animated examples of how this property changes the + /// behavior of the app bar in combination with [floating]. + final bool pinned; + + /// The material's shape as well its shadow. + /// + /// A shadow is only displayed if the [elevation] is greater than + /// zero. + final ShapeBorder? shape; + + /// If [snap] and [floating] are true then the floating app bar will "snap" + /// into view. + /// + /// If [snap] is true then a scroll that exposes the floating app bar will + /// trigger an animation that slides the entire app bar into view. Similarly if + /// a scroll dismisses the app bar, the animation will slide the app bar + /// completely out of view. + /// + /// Snapping only applies when the app bar is floating, not when the appbar + /// appears at the top of its scroll view. + /// + /// ## Animated Examples + /// + /// The following animations show how the app bar changes its scrolling + /// behavior based on the value of this property. + /// + /// * App bar with [snap] set to false: + /// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_floating.mp4} + /// * App bar with [snap] set to true: + /// {@animation 476 400 https://flutter.github.io/assets-for-api-docs/assets/material/app_bar_floating_snap.mp4} + /// + /// See also: + /// + /// * [SliverNewGradientAppBar] for more animated examples of how this property changes the + /// behavior of the app bar in combination with [pinned] and [floating]. + final bool snap; + + @override + _SliverNewGradientAppBarState createState() => + _SliverNewGradientAppBarState(); +} + +// This class is only Stateful because it owns the TickerProvider used +// by the floating appbar snap animation (via FloatingHeaderSnapConfiguration). +class _SliverNewGradientAppBarState extends State + with TickerProviderStateMixin { + FloatingHeaderSnapConfiguration? _snapConfiguration; + + void _updateSnapConfiguration() { + if (widget.snap && widget.floating) { + _snapConfiguration = FloatingHeaderSnapConfiguration( + curve: Curves.easeOut, + duration: const Duration(milliseconds: 200), + ); + } else { + _snapConfiguration = null; } - return actionButtons; + } + + @override + void initState() { + super.initState(); + _updateSnapConfiguration(); + } + + @override + void didUpdateWidget(SliverNewGradientAppBar oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.snap != oldWidget.snap || widget.floating != oldWidget.floating) + _updateSnapConfiguration(); + } + + @override + Widget build(BuildContext context) { + assert(!widget.primary || debugCheckHasMediaQuery(context)); + final double topPadding = + widget.primary ? MediaQuery.of(context).padding.top : 0.0; + final double collapsedHeight = ((widget.pinned && widget.floating) + ? widget.bottom!.preferredSize.height + topPadding + : null)!; + + return MediaQuery.removePadding( + context: context, + removeBottom: true, + child: SliverPersistentHeader( + floating: widget.floating, + pinned: widget.pinned, + delegate: _SliverGradientAppBarDelegate( + leading: (widget.leading)!, + automaticallyImplyLeading: widget.automaticallyImplyLeading, + title: widget.title!, + actions: widget.actions!, + flexibleSpace: widget.flexibleSpace!, + bottom: widget.bottom!, + elevation: widget.elevation!, + forceElevated: widget.forceElevated, + gradient: widget.gradient!, + brightness: widget.brightness!, + iconTheme: widget.iconTheme!, + actionsIconTheme: widget.actionsIconTheme!, + textTheme: widget.textTheme!, + primary: widget.primary, + centerTitle: widget.centerTitle!, + titleSpacing: widget.titleSpacing, + expandedHeight: widget.expandedHeight!, + collapsedHeight: collapsedHeight, + topPadding: topPadding, + floating: widget.floating, + pinned: widget.pinned, + shape: widget.shape!, + snapConfiguration: (_snapConfiguration)!, + ), + ), + ); } } From 3f74e38aa2e4248197584e57d3697bc4e1db9f4a Mon Sep 17 00:00:00 2001 From: Baig Date: Fri, 19 May 2023 16:30:44 +0500 Subject: [PATCH 11/12] Update pubspec.yaml version 0.3.0 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 108cbda..2244c0c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: new_gradient_app_bar description: The official AppBar, with a more colorful twist. Add gradients to spice up your application and make it beautiful. -version: 0.2.0 +version: 0.3.0 homepage: https://github.com/GUIKAR741/NewGradientAppBar dependencies: From ec4993528c59e8952889ce9cd9d732261271ac10 Mon Sep 17 00:00:00 2001 From: Baig Date: Fri, 19 May 2023 16:32:27 +0500 Subject: [PATCH 12/12] Update CHANGELOG.md 0.3.0 --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d227a26..53c37bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## [0.3.0] - May 2023 + + - Update Dart 3.10.1 [#1](https://github.com/GUIKAR741/NewGradientAppBar/pull/1) + - Update using dart fix + - ## [0.2.0] - April 2021 - Update Dart 2.12.0 [#1](https://github.com/GUIKAR741/NewGradientAppBar/pull/1)