From a3dfd3cef17bb7dd439cf918fc2a8ebf7b9df1bc Mon Sep 17 00:00:00 2001 From: mikecoomber Date: Tue, 15 Oct 2024 14:14:40 +0100 Subject: [PATCH] removed extended search appbar, changed behaviour to close search --- .../pages/components/top_app_bar_example.dart | 78 +------- .../components/top_app_bar_widgetbook.dart | 2 +- .../screen_header_bar/screen_header_bar.dart | 2 +- .../top_app_bar/extended_top_app_bar.dart | 50 +++-- .../top_app_bar/search_top_app_bar.dart | 10 +- .../components/top_app_bar/top_app_bar.dart | 178 ++++++++++++------ 6 files changed, 150 insertions(+), 170 deletions(-) diff --git a/example/lib/pages/components/top_app_bar_example.dart b/example/lib/pages/components/top_app_bar_example.dart index 6adc0827..5c5d30fe 100644 --- a/example/lib/pages/components/top_app_bar_example.dart +++ b/example/lib/pages/components/top_app_bar_example.dart @@ -15,20 +15,7 @@ class TopAppBarExample extends StatefulWidget { } class _TopAppBarExampleState extends State { - final _searchControllerExtended = ZetaSearchController(); - final _searchControllerRegular = ZetaSearchController(); - - void _showHideSearchExtended() { - _searchControllerExtended.isEnabled - ? _searchControllerExtended.closeSearch() - : _searchControllerExtended.startSearch(); - } - - void _showHideSearchRegular() { - _searchControllerRegular.isEnabled - ? _searchControllerRegular.closeSearch() - : _searchControllerRegular.startSearch(); - } + final _searchController = ZetaSearchController(); @override Widget build(BuildContext context) { @@ -78,8 +65,7 @@ class _TopAppBarExampleState extends State { ], ), Text('Centered', style: ZetaTextStyles.titleLarge), - ZetaTopAppBar( - type: ZetaTopAppBarType.centeredTitle, + ZetaTopAppBar.centered( leading: IconButton( onPressed: () {}, icon: ZetaIcon(Icons.menu), @@ -119,24 +105,20 @@ class _TopAppBarExampleState extends State { ], ), Text('Search', style: ZetaTextStyles.titleLarge), - ZetaTopAppBar( - type: ZetaTopAppBarType.centeredTitle, - leading: BackButton(), + ZetaTopAppBar.search( + leading: IconButton( + onPressed: () {}, + icon: ZetaIcon(Icons.menu), + ), title: Text("Title"), - actions: [ - IconButton( - onPressed: _showHideSearchRegular, - icon: ZetaIcon(ZetaIcons.search), - ) - ], - searchController: _searchControllerRegular, + searchController: _searchController, onSearch: (text) => debugPrint('search text: $text'), onSearchMicrophoneIconPressed: () async { var sampleTexts = ['This is a sample text', 'Another sample', 'Speech recognition text', 'Example']; var generatedText = sampleTexts[Random().nextInt(sampleTexts.length)]; - _searchControllerRegular.text = generatedText; + _searchController.text = generatedText; }, ), Text('Extended', style: ZetaTextStyles.titleLarge), @@ -188,48 +170,6 @@ class _TopAppBarExampleState extends State { ], ), ), - Text('Extended Search', style: ZetaTextStyles.titleLarge), - SizedBox( - width: 800, - height: 200, - child: CustomScrollView( - slivers: [ - ZetaTopAppBar.extended( - leading: BackButton(), - title: Text("Title"), - actions: [ - IconButton( - onPressed: _showHideSearchExtended, - icon: ZetaIcon(ZetaIcons.search), - ) - ], - searchController: _searchControllerExtended, - onSearch: (text) => debugPrint('search text: $text'), - onSearchMicrophoneIconPressed: () async { - var sampleTexts = [ - 'This is a sample text', - 'Another sample', - 'Speech recognition text', - 'Example' - ]; - var generatedText = sampleTexts[Random().nextInt(sampleTexts.length)]; - _searchControllerExtended.text = generatedText; - }, - ), - SliverToBoxAdapter( - child: Container( - width: 800, - height: 800, - color: Zeta.of(context).colors.surfaceSelectedHover, - child: CustomPaint( - painter: Painter(context: context), - size: Size(800, 800), - ), - ), - ), - ], - ), - ), ].gap(20), ), ), diff --git a/example/widgetbook/pages/components/top_app_bar_widgetbook.dart b/example/widgetbook/pages/components/top_app_bar_widgetbook.dart index 96f80616..ea4ba12a 100644 --- a/example/widgetbook/pages/components/top_app_bar_widgetbook.dart +++ b/example/widgetbook/pages/components/top_app_bar_widgetbook.dart @@ -53,7 +53,7 @@ Widget defaultTopAppBarUseCase(BuildContext context) { icon: ZetaIcon(ZetaIcons.more_vertical), ) ] - : null, + : [], ), ], )); diff --git a/lib/src/components/screen_header_bar/screen_header_bar.dart b/lib/src/components/screen_header_bar/screen_header_bar.dart index 544eefe6..4212abf0 100644 --- a/lib/src/components/screen_header_bar/screen_header_bar.dart +++ b/lib/src/components/screen_header_bar/screen_header_bar.dart @@ -50,7 +50,7 @@ class ZetaScreenHeaderBar extends ZetaStatelessWidget { title: title, titleTextStyle: ZetaTextStyles.titleLarge, actions: actionButtonLabel == null - ? null + ? [] : [ ZetaButton( label: actionButtonLabel!, diff --git a/lib/src/components/top_app_bar/extended_top_app_bar.dart b/lib/src/components/top_app_bar/extended_top_app_bar.dart index b2577646..c6a8d289 100644 --- a/lib/src/components/top_app_bar/extended_top_app_bar.dart +++ b/lib/src/components/top_app_bar/extended_top_app_bar.dart @@ -15,7 +15,6 @@ class ZetaExtendedAppBarDelegate extends SliverPersistentHeaderDelegate { required this.shrinks, this.actions, this.leading, - this.searchController, }); /// Title of the app bar. @@ -27,56 +26,49 @@ class ZetaExtendedAppBarDelegate extends SliverPersistentHeaderDelegate { /// Widget displayed first in the app bar row. final Widget? leading; - /// Used to control the search textfield and states. - final ZetaSearchController? searchController; - /// If `ZetaTopAppBarType.extend` shrinks. Does not affect other types of app bar. final bool shrinks; static const double _maxExtent = 104; - static const double _minExtent = 52; + static const double _minExtent = 56; @override Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { - final searchBarOffsetTop = Zeta.of(context).spacing.minimum * 1.5; - final searchBarOffsetRight = Zeta.of(context).spacing.minimum * 22; - final maxExtent = Zeta.of(context).spacing.minimum * 26; - final leftMin = Zeta.of(context).spacing.large; - final topMin = Zeta.of(context).spacing.xl; - final topMax = Zeta.of(context).spacing.minimum * 15; + final spacing = Zeta.of(context).spacing; + + final maxExtent = spacing.minimum * 26; + final leftMin = spacing.large; + final topMin = spacing.xl; + final topMax = spacing.minimum * 15; /// If there is no leading widget, the left margin should not change /// If there is a leading widget, the left margin should be the same as the leading widget's width plus padding - final leftMax = leading == null ? leftMin : _minExtent + Zeta.of(context).spacing.small; + final leftMax = leading == null ? leftMin : _minExtent + spacing.small; + + final top = shrinks + ? (topMax + (-1 * shrinkOffset)).clamp( + topMin - (spacing.minimum), + topMax - (spacing.minimum), + ) + : topMax; return ConstrainedBox( - constraints: BoxConstraints(minHeight: Zeta.of(context).spacing.xl_9, maxHeight: maxExtent), + constraints: BoxConstraints(minHeight: spacing.xl_9, maxHeight: maxExtent), child: ColoredBox( color: Zeta.of(context).colors.surfacePrimary, child: Stack( children: [ Positioned( - top: shrinks - ? (topMax + (-1 * shrinkOffset)).clamp( - topMin - - (searchController != null && searchController!.isEnabled - ? searchBarOffsetTop - : Zeta.of(context).spacing.none), - topMax, - ) - : topMax, + top: top, left: shrinks ? ((shrinkOffset / maxExtent) * _maxExtent).clamp(leftMin, leftMax) : leftMin, - right: searchController != null && searchController!.isEnabled - ? searchBarOffsetRight - : Zeta.of(context).spacing.none, + right: spacing.none, child: title, ), - if (leading != null) - Positioned(top: Zeta.of(context).spacing.medium, left: Zeta.of(context).spacing.small, child: leading!), + if (leading != null) Positioned(top: spacing.small, left: spacing.small, child: leading!), if (actions != null) Positioned( - top: Zeta.of(context).spacing.medium, - right: Zeta.of(context).spacing.small, + top: spacing.small, + right: spacing.small, child: Row(children: actions!), ), ], diff --git a/lib/src/components/top_app_bar/search_top_app_bar.dart b/lib/src/components/top_app_bar/search_top_app_bar.dart index a05b15f4..7507541f 100644 --- a/lib/src/components/top_app_bar/search_top_app_bar.dart +++ b/lib/src/components/top_app_bar/search_top_app_bar.dart @@ -66,20 +66,12 @@ class _ZetaTopAppBarSearchFieldState extends State wit @override void initState() { - _textFocusNode.addListener(_onFocusChanged); widget.searchController?.addListener(_onSearchControllerChanged); widget.searchController?.textEditingController ??= TextEditingController(); super.initState(); } - void _onFocusChanged() { - final text = widget.searchController?.text ?? ''; - final shouldCloseSearch = _isSearching && text.isEmpty && !_textFocusNode.hasFocus; - - if (shouldCloseSearch) _closeSearch(); - } - void _onSearchControllerChanged() { final controller = widget.searchController; if (controller == null) return; @@ -151,7 +143,7 @@ class _ZetaTopAppBarSearchFieldState extends State wit children: [ Row( mainAxisAlignment: - widget.type == ZetaTopAppBarType.centeredTitle ? MainAxisAlignment.center : MainAxisAlignment.start, + widget.type == ZetaTopAppBarType.centered ? MainAxisAlignment.center : MainAxisAlignment.start, children: [ widget.child ?? const Nothing(), ], diff --git a/lib/src/components/top_app_bar/top_app_bar.dart b/lib/src/components/top_app_bar/top_app_bar.dart index 21f11e5b..e2de918e 100644 --- a/lib/src/components/top_app_bar/top_app_bar.dart +++ b/lib/src/components/top_app_bar/top_app_bar.dart @@ -18,32 +18,58 @@ class ZetaTopAppBar extends ZetaStatefulWidget implements PreferredSizeWidget { const ZetaTopAppBar({ super.key, super.rounded, - this.actions, + this.actions = const [], this.automaticallyImplyLeading = true, - this.searchController, this.leading, this.title, this.titleTextStyle, this.type = ZetaTopAppBarType.defaultAppBar, - this.onSearch, - this.searchHintText = 'Search', - this.onSearchMicrophoneIconPressed, - }) : shrinks = false; + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ValueChanged? onSearch, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') String? searchHintText, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ZetaSearchController? searchController, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') + VoidCallback? onSearchMicrophoneIconPressed, + }) : shrinks = false, + onSearch = null, + searchHintText = null, + searchController = null, + onSearchMicrophoneIconPressed = null; /// Creates a ZetaTopAppBar with centered title. const ZetaTopAppBar.centered({ super.key, super.rounded, - this.actions, + this.actions = const [], + this.automaticallyImplyLeading = true, + this.leading, + this.title, + this.titleTextStyle, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ValueChanged? onSearch, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') String? searchHintText, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ZetaSearchController? searchController, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') + VoidCallback? onSearchMicrophoneIconPressed, + }) : type = ZetaTopAppBarType.centered, + onSearch = null, + searchHintText = null, + searchController = null, + onSearchMicrophoneIconPressed = null, + shrinks = false; + + /// Creates a ZetaTopAppBar with an expanding search field. + const ZetaTopAppBar.search({ + super.key, + super.rounded, this.automaticallyImplyLeading = true, this.searchController, this.leading, this.title, this.titleTextStyle, this.onSearch, - this.searchHintText = 'Search', + this.searchHintText, this.onSearchMicrophoneIconPressed, - }) : type = ZetaTopAppBarType.centeredTitle, + this.actions = const [], + }) : type = ZetaTopAppBarType.centered, shrinks = false; /// Creates a ZetaTopAppBar with an extended title over 2 lines. @@ -52,23 +78,28 @@ class ZetaTopAppBar extends ZetaStatefulWidget implements PreferredSizeWidget { const ZetaTopAppBar.extended({ super.key, super.rounded, - this.actions, + this.actions = const [], this.automaticallyImplyLeading = true, - this.searchController, this.leading, this.title, this.titleTextStyle, - this.onSearch, - this.searchHintText = 'Search', - this.onSearchMicrophoneIconPressed, this.shrinks = true, - }) : type = ZetaTopAppBarType.extendedTitle; + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ValueChanged? onSearch, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') String? searchHintText, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') ZetaSearchController? searchController, + @Deprecated('Use ZetaTopAppBar.search instead. ' 'Deprecated as of 0.16.0') + VoidCallback? onSearchMicrophoneIconPressed, + }) : type = ZetaTopAppBarType.extended, + onSearch = null, + searchHintText = null, + onSearchMicrophoneIconPressed = null, + searchController = null; /// Called when text in the search field is submitted. - final void Function(String)? onSearch; + final ValueChanged? onSearch; /// A list of Widgets to display in a row after the [title] widget. - final List? actions; + final List actions; /// Configures whether the back button to be displayed. final bool automaticallyImplyLeading; @@ -118,29 +149,25 @@ class ZetaTopAppBar extends ZetaStatefulWidget implements PreferredSizeWidget { } class _ZetaTopAppBarState extends State { - bool _isSearchEnabled = false; + late ZetaSearchController _searchController; + bool get _searchEnabled => widget.searchController != null || widget.onSearch != null; + bool get _searchActive => _searchController.isEnabled; @override void initState() { - widget.searchController?.addListener(_onSearchControllerChanged); + _searchController = widget.searchController ?? ZetaSearchController(); + _searchController.addListener(() => setState(() {})); super.initState(); } - void _onSearchControllerChanged() { - final controller = widget.searchController; - if (controller == null) return; - - setState(() => _isSearchEnabled = controller.isEnabled); - } - @override void dispose() { - widget.searchController?.removeListener(_onSearchControllerChanged); + _searchController.dispose(); super.dispose(); } Widget _getTitleText(ZetaColors colors) { - var title = widget.title; + Widget? title = widget.title; if (widget.title is Row) { final oldRow = widget.title! as Row; title = Row( @@ -169,35 +196,48 @@ class _ZetaTopAppBarState extends State { } List? _getActions(ZetaColors colors) { - return _isSearchEnabled - ? [ - IconButtonTheme( - data: IconButtonThemeData( - style: IconButton.styleFrom(iconSize: Zeta.of(context).spacing.xl), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - color: colors.cool.shade50, - onPressed: () => widget.searchController?.clearText(), - icon: const ZetaIcon(ZetaIcons.cancel), - ), - if (widget.onSearchMicrophoneIconPressed != null) ...[ - SizedBox( - height: Zeta.of(context).spacing.xl_2, - child: VerticalDivider(width: ZetaBorders.medium, color: colors.cool.shade70), - ), - IconButton( - onPressed: widget.onSearchMicrophoneIconPressed, - icon: const ZetaIcon(ZetaIcons.microphone), - ), - ], - ], + if (_searchActive) { + return [ + IconButtonTheme( + data: IconButtonThemeData( + style: IconButton.styleFrom(iconSize: Zeta.of(context).spacing.xl), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + color: colors.cool.shade50, + onPressed: () => _searchController.clearText(), + icon: const ZetaIcon(ZetaIcons.cancel), ), - ), - ] - : widget.actions; + if (widget.onSearchMicrophoneIconPressed != null) ...[ + SizedBox( + height: Zeta.of(context).spacing.xl_2, + child: VerticalDivider(width: ZetaBorders.medium, color: colors.cool.shade70), + ), + IconButton( + onPressed: widget.onSearchMicrophoneIconPressed, + icon: const ZetaIcon(ZetaIcons.microphone), + ), + ], + ], + ), + ), + ]; + } + + if (_searchEnabled) { + return [ + ...widget.actions, + IconButton( + onPressed: () => setState(() { + _searchController.startSearch(); + }), + icon: const ZetaIcon(ZetaIcons.search), + ), + ]; + } + return widget.actions; } @override @@ -213,24 +253,32 @@ class _ZetaTopAppBarState extends State { hintText: widget.searchHintText ?? 'Search', onSearch: widget.onSearch, type: widget.type, - isExtended: widget.type == ZetaTopAppBarType.extendedTitle, + isExtended: widget.type == ZetaTopAppBarType.extended, child: titleText, ) : titleText; - if (widget.type == ZetaTopAppBarType.extendedTitle) { + if (widget.type == ZetaTopAppBarType.extended) { return SliverPersistentHeader( pinned: true, delegate: ZetaExtendedAppBarDelegate( actions: actions, leading: widget.leading, - searchController: widget.searchController, title: title, shrinks: widget.shrinks, ), ); } + Widget? leading = widget.leading; + + if (_searchActive) { + leading = IconButton( + onPressed: _searchController.closeSearch, + icon: const ZetaIcon(ZetaIcons.arrow_back), + ); + } + return ZetaRoundedScope( rounded: context.rounded, child: ColoredBox( @@ -244,10 +292,10 @@ class _ZetaTopAppBarState extends State { scrolledUnderElevation: 0, iconTheme: IconThemeData(color: colors.cool.shade90), leadingWidth: Zeta.of(context).spacing.xl_6, - leading: widget.leading, + leading: leading, automaticallyImplyLeading: widget.automaticallyImplyLeading, surfaceTintColor: Colors.transparent, - centerTitle: widget.type == ZetaTopAppBarType.centeredTitle, + centerTitle: widget.type == ZetaTopAppBarType.centered, titleTextStyle: widget.titleTextStyle == null ? ZetaTextStyles.bodyLarge.copyWith(color: colors.textDefault) : widget.titleTextStyle!.copyWith(color: colors.textDefault), @@ -267,8 +315,16 @@ enum ZetaTopAppBarType { defaultAppBar, /// Title in the center. + @Deprecated('Use ZetaTopAppBar.centered instead. ' 'Deprecated as of 0.16.0') centeredTitle, + /// Aligns the title to the center of the app bar. + centered, + /// Title below the app bar. + @Deprecated('Use ZetaTopAppBar.extended instead. ' 'Deprecated as of 0.16.0') extendedTitle, + + /// Title extends over 2 lines and collapses when scrolled. + extended, }