home_screen.dart 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. import 'package:app_flowy/startup/plugin/plugin.dart';
  2. import 'package:app_flowy/workspace/application/home/home_bloc.dart';
  3. import 'package:app_flowy/workspace/presentation/widgets/edit_panel/panel_animation.dart';
  4. import 'package:app_flowy/workspace/presentation/widgets/float_bubble/question_bubble.dart';
  5. import 'package:app_flowy/startup/startup.dart';
  6. import 'package:flowy_sdk/log.dart';
  7. import 'package:flowy_infra_ui/style_widget/container.dart';
  8. import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB;
  9. import 'package:flowy_sdk/protobuf/flowy-folder/protobuf.dart';
  10. import 'package:flutter/gestures.dart';
  11. import 'package:flutter/material.dart';
  12. import 'package:flutter_bloc/flutter_bloc.dart';
  13. import 'package:styled_widget/styled_widget.dart';
  14. import '../widgets/edit_panel/edit_panel.dart';
  15. import 'home_layout.dart';
  16. import 'home_stack.dart';
  17. import 'menu/menu.dart';
  18. class HomeScreen extends StatefulWidget {
  19. final UserProfilePB user;
  20. final CurrentWorkspaceSettingPB workspaceSetting;
  21. const HomeScreen(this.user, this.workspaceSetting, {Key? key})
  22. : super(key: key);
  23. @override
  24. State<HomeScreen> createState() => _HomeScreenState();
  25. }
  26. class _HomeScreenState extends State<HomeScreen> {
  27. ViewPB? initialView;
  28. @override
  29. void initState() {
  30. super.initState();
  31. }
  32. @override
  33. void didUpdateWidget(covariant HomeScreen oldWidget) {
  34. initialView = null;
  35. super.didUpdateWidget(oldWidget);
  36. }
  37. @override
  38. Widget build(BuildContext context) {
  39. return MultiBlocProvider(
  40. providers: [
  41. BlocProvider<HomeBloc>(
  42. create: (context) {
  43. return HomeBloc(widget.user, widget.workspaceSetting)
  44. ..add(const HomeEvent.initial());
  45. },
  46. ),
  47. ],
  48. child: Scaffold(
  49. body: BlocListener<HomeBloc, HomeState>(
  50. listenWhen: (p, c) => p.unauthorized != c.unauthorized,
  51. listener: (context, state) {
  52. if (state.unauthorized) {
  53. Log.error("Push to login screen when user token was invalid");
  54. }
  55. },
  56. child: BlocBuilder<HomeBloc, HomeState>(
  57. buildWhen: (previous, current) => previous != current,
  58. builder: (context, state) {
  59. final collapsedNotifier =
  60. getIt<HomeStackManager>().collapsedNotifier;
  61. collapsedNotifier.addPublishListener((isCollapsed) {
  62. context
  63. .read<HomeBloc>()
  64. .add(HomeEvent.forceCollapse(isCollapsed));
  65. });
  66. return FlowyContainer(
  67. Theme.of(context).colorScheme.surface,
  68. // Colors.white,
  69. child: _buildBody(state),
  70. );
  71. },
  72. ),
  73. ),
  74. ),
  75. );
  76. }
  77. Widget _buildBody(HomeState state) {
  78. return LayoutBuilder(
  79. builder: (BuildContext context, BoxConstraints constraints) {
  80. final layout = HomeLayout(context, constraints, state.forceCollapse);
  81. const homeStack = HomeStack();
  82. final menu = _buildHomeMenu(
  83. layout: layout,
  84. context: context,
  85. state: state,
  86. );
  87. final homeMenuResizer = _buildHomeMenuResizer(context: context);
  88. final editPanel = _buildEditPanel(
  89. homeState: state,
  90. layout: layout,
  91. context: context,
  92. );
  93. const bubble = QuestionBubble();
  94. return _layoutWidgets(
  95. layout: layout,
  96. homeStack: homeStack,
  97. homeMenu: menu,
  98. editPanel: editPanel,
  99. bubble: bubble,
  100. homeMenuResizer: homeMenuResizer,
  101. );
  102. },
  103. );
  104. }
  105. Widget _buildHomeMenu(
  106. {required HomeLayout layout,
  107. required BuildContext context,
  108. required HomeState state}) {
  109. final workspaceSetting = state.workspaceSetting;
  110. if (initialView == null && workspaceSetting.hasLatestView()) {
  111. initialView = workspaceSetting.latestView;
  112. final plugin = makePlugin(
  113. pluginType: initialView!.pluginType,
  114. data: initialView,
  115. );
  116. getIt<HomeStackManager>().setPlugin(plugin);
  117. }
  118. HomeMenu homeMenu = HomeMenu(
  119. user: widget.user,
  120. workspaceSetting: workspaceSetting,
  121. collapsedNotifier: getIt<HomeStackManager>().collapsedNotifier,
  122. );
  123. final latestView =
  124. workspaceSetting.hasLatestView() ? workspaceSetting.latestView : null;
  125. if (getIt<MenuSharedState>().latestOpenView == null) {
  126. /// AppFlowy will open the view that the last time the user opened it. The _buildHomeMenu will get called when AppFlowy's screen resizes. So we only set the latestOpenView when it's null.
  127. getIt<MenuSharedState>().latestOpenView = latestView;
  128. }
  129. return FocusTraversalGroup(child: RepaintBoundary(child: homeMenu));
  130. }
  131. Widget _buildEditPanel(
  132. {required HomeState homeState,
  133. required BuildContext context,
  134. required HomeLayout layout}) {
  135. final homeBloc = context.read<HomeBloc>();
  136. return BlocBuilder<HomeBloc, HomeState>(
  137. buildWhen: (previous, current) =>
  138. previous.panelContext != current.panelContext,
  139. builder: (context, state) {
  140. return state.panelContext.fold(
  141. () => const SizedBox(),
  142. (panelContext) => FocusTraversalGroup(
  143. child: RepaintBoundary(
  144. child: EditPanel(
  145. panelContext: panelContext,
  146. onEndEdit: () =>
  147. homeBloc.add(const HomeEvent.dismissEditPanel()),
  148. ),
  149. ),
  150. ),
  151. );
  152. },
  153. );
  154. }
  155. Widget _buildHomeMenuResizer({
  156. required BuildContext context,
  157. }) {
  158. return MouseRegion(
  159. cursor: SystemMouseCursors.resizeLeftRight,
  160. child: GestureDetector(
  161. dragStartBehavior: DragStartBehavior.down,
  162. onPanUpdate: ((details) {
  163. context
  164. .read<HomeBloc>()
  165. .add(HomeEvent.editPanelResized(details.delta.dx));
  166. }),
  167. behavior: HitTestBehavior.translucent,
  168. child: SizedBox(
  169. width: 10,
  170. height: MediaQuery.of(context).size.height,
  171. )),
  172. );
  173. }
  174. Widget _layoutWidgets({
  175. required HomeLayout layout,
  176. required Widget homeMenu,
  177. required Widget homeStack,
  178. required Widget editPanel,
  179. required Widget bubble,
  180. required Widget homeMenuResizer,
  181. }) {
  182. return Stack(
  183. children: [
  184. homeMenu
  185. .animatedPanelX(
  186. closeX: -layout.menuWidth,
  187. isClosed: !layout.showMenu,
  188. )
  189. .positioned(
  190. left: 0,
  191. top: 0,
  192. width: layout.menuWidth,
  193. bottom: 0,
  194. animate: true)
  195. .animate(layout.animDuration, Curves.easeOut),
  196. homeStack
  197. .constrained(minWidth: 500)
  198. .positioned(
  199. left: layout.homePageLOffset,
  200. right: layout.homePageROffset,
  201. bottom: 0,
  202. top: 0,
  203. animate: true)
  204. .animate(layout.animDuration, Curves.easeOut),
  205. homeMenuResizer.positioned(left: layout.homePageLOffset - 5),
  206. bubble
  207. .positioned(
  208. right: 20,
  209. bottom: 16,
  210. animate: true,
  211. )
  212. .animate(layout.animDuration, Curves.easeOut),
  213. editPanel
  214. .animatedPanelX(
  215. duration: layout.animDuration.inMilliseconds * 0.001,
  216. closeX: layout.editPanelWidth,
  217. isClosed: !layout.showEditPanel,
  218. )
  219. .positioned(
  220. right: 0, top: 0, bottom: 0, width: layout.editPanelWidth),
  221. ],
  222. );
  223. }
  224. }