diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/folder/personal_folder.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/folder/personal_folder.dart index 4aa2d41355..c8a44d2f75 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/folder/personal_folder.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/folder/personal_folder.dart @@ -1,3 +1,6 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/workspace/application/menu/menu_bloc.dart'; @@ -8,17 +11,17 @@ import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; class PersonalFolder extends StatelessWidget { const PersonalFolder({ super.key, required this.views, + this.isHoverEnabled = true, }); final List views; + final bool isHoverEnabled; @override Widget build(BuildContext context) { @@ -60,6 +63,7 @@ class PersonalFolder extends StatelessWidget { }, onTertiarySelected: (view) => context.read().openTab(view), + isHoverEnabled: isHoverEnabled, ), ), ], diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar.dart index 80cee5be5e..8a284464b8 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar.dart @@ -1,3 +1,7 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + import 'package:appflowy/shared/feature_flags.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart'; @@ -18,7 +22,6 @@ import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart' show UserProfilePB; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; -import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; /// Home Sidebar is the left side bar of the home page. @@ -28,7 +31,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; /// - settings /// - scrollable document list /// - trash -class HomeSideBar extends StatelessWidget { +class HomeSideBar extends StatefulWidget { const HomeSideBar({ super.key, required this.userProfile, @@ -39,6 +42,43 @@ class HomeSideBar extends StatelessWidget { final WorkspaceSettingPB workspaceSetting; + @override + State createState() => _HomeSideBarState(); +} + +class _HomeSideBarState extends State { + final _scrollController = ScrollController(); + Timer? _srollDebounce; + bool isScrolling = false; + + @override + void initState() { + super.initState(); + _scrollController.addListener(_onScrollChanged); + } + + void _onScrollChanged() { + setState(() => isScrolling = true); + + _srollDebounce?.cancel(); + _srollDebounce = + Timer(const Duration(milliseconds: 300), _setScrollStopped); + } + + void _setScrollStopped() { + if (mounted) { + setState(() => isScrolling = false); + } + } + + @override + void dispose() { + _srollDebounce?.cancel(); + _scrollController.removeListener(_onScrollChanged); + _scrollController.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { return MultiBlocProvider( @@ -48,8 +88,8 @@ class HomeSideBar extends StatelessWidget { ), BlocProvider( create: (_) => MenuBloc( - user: userProfile, - workspaceId: workspaceSetting.workspaceId, + user: widget.userProfile, + workspaceId: widget.workspaceSetting.workspaceId, )..add(const MenuEvent.initial()), ), ], @@ -108,8 +148,14 @@ class HomeSideBar extends StatelessWidget { Padding( padding: menuHorizontalInset, child: FeatureFlag.collaborativeWorkspace.isOn - ? SidebarWorkspace(userProfile: userProfile, views: views) - : SidebarUser(userProfile: userProfile, views: views), + ? SidebarWorkspace( + userProfile: widget.userProfile, + views: views, + ) + : SidebarUser( + userProfile: widget.userProfile, + views: views, + ), ), const VSpace(20), @@ -118,9 +164,12 @@ class HomeSideBar extends StatelessWidget { child: Padding( padding: menuHorizontalInset, child: SingleChildScrollView( + controller: _scrollController, + physics: const ClampingScrollPhysics(), child: SidebarFolder( views: views, favoriteViews: favoriteViews, + isHoverEnabled: !isScrolling, ), ), ), diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar_folder.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar_folder.dart index 01ec648e74..397a3e3d90 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar_folder.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar_folder.dart @@ -1,20 +1,23 @@ +import 'package:flutter/material.dart'; + import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/favorite_folder.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/personal_folder.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flutter/material.dart'; class SidebarFolder extends StatelessWidget { const SidebarFolder({ super.key, required this.views, required this.favoriteViews, + this.isHoverEnabled = true, }); final List views; final List favoriteViews; + final bool isHoverEnabled; @override Widget build(BuildContext context) { @@ -38,7 +41,7 @@ class SidebarFolder extends StatelessWidget { const VSpace(10), ], // personal - PersonalFolder(views: views), + PersonalFolder(views: views, isHoverEnabled: isHoverEnabled), ], ); }, diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart index 3a2afacbfe..2cdc373181 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_item.dart @@ -42,6 +42,7 @@ class ViewItem extends StatelessWidget { this.isDraggable = true, required this.isFeedback, this.height = 28.0, + this.isHoverEnabled = true, }); final ViewPB view; @@ -75,6 +76,8 @@ class ViewItem extends StatelessWidget { final double height; + final bool isHoverEnabled; + @override Widget build(BuildContext context) { return BlocProvider( @@ -101,6 +104,7 @@ class ViewItem extends StatelessWidget { isDraggable: isDraggable, isFeedback: isFeedback, height: height, + isHoverEnabled: isHoverEnabled, ); }, ), @@ -127,6 +131,7 @@ class InnerViewItem extends StatelessWidget { this.isFirstChild = false, required this.isFeedback, required this.height, + this.isHoverEnabled = true, }); final ViewPB view; @@ -148,6 +153,8 @@ class InnerViewItem extends StatelessWidget { final ViewItemOnSelected? onTertiarySelected; final double height; + final bool isHoverEnabled; + @override Widget build(BuildContext context) { Widget child = SingleInnerViewItem( @@ -264,6 +271,7 @@ class SingleInnerViewItem extends StatefulWidget { this.onTertiarySelected, required this.isFeedback, required this.height, + this.isHoverEnabled = true, }); final ViewPB view; @@ -282,6 +290,8 @@ class SingleInnerViewItem extends StatefulWidget { final FolderCategoryType categoryType; final double height; + final bool isHoverEnabled; + @override State createState() => _SingleInnerViewItemState(); } @@ -292,13 +302,16 @@ class _SingleInnerViewItemState extends State { @override Widget build(BuildContext context) { - if (widget.isFeedback) { - return _buildViewItem(false); - } - final isSelected = getIt().latestOpenView?.id == widget.view.id; + if (widget.isFeedback || !widget.isHoverEnabled) { + return _buildViewItem( + false, + !widget.isHoverEnabled ? isSelected : false, + ); + } + return FlowyHover( style: HoverStyle( hoverColor: Theme.of(context).colorScheme.secondary,