home_screen.dart 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. import 'package:app_flowy/plugin/plugin.dart';
  2. import 'package:app_flowy/workspace/application/home/home_bloc.dart';
  3. import 'package:app_flowy/workspace/presentation/widgets/edit_pannel/pannel_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_pannel/edit_pannel.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}) : super(key: key);
  22. @override
  23. State<HomeScreen> createState() => _HomeScreenState();
  24. }
  25. class _HomeScreenState extends State<HomeScreen> {
  26. ViewPB? initialView;
  27. @override
  28. void initState() {
  29. super.initState();
  30. }
  31. @override
  32. void didUpdateWidget(covariant HomeScreen oldWidget) {
  33. initialView = null;
  34. super.didUpdateWidget(oldWidget);
  35. }
  36. @override
  37. Widget build(BuildContext context) {
  38. return MultiBlocProvider(
  39. providers: [
  40. BlocProvider<HomeBloc>(
  41. create: (context) {
  42. return HomeBloc(widget.user, widget.workspaceSetting)..add(const HomeEvent.initial());
  43. },
  44. ),
  45. ],
  46. child: Scaffold(
  47. body: BlocListener<HomeBloc, HomeState>(
  48. listenWhen: (p, c) => p.unauthorized != c.unauthorized,
  49. listener: (context, state) {
  50. if (state.unauthorized) {
  51. Log.error("Push to login screen when user token was invalid");
  52. }
  53. },
  54. child: BlocBuilder<HomeBloc, HomeState>(
  55. buildWhen: (previous, current) => previous != current,
  56. builder: (context, state) {
  57. final collapasedNotifier = getIt<HomeStackManager>().collapsedNotifier;
  58. collapasedNotifier.addPublishListener((isCollapsed) {
  59. context.read<HomeBloc>().add(HomeEvent.forceCollapse(isCollapsed));
  60. });
  61. return FlowyContainer(
  62. Theme.of(context).colorScheme.surface,
  63. // Colors.white,
  64. child: _buildBody(state),
  65. );
  66. },
  67. ),
  68. ),
  69. ),
  70. );
  71. }
  72. Widget _buildBody(HomeState state) {
  73. return LayoutBuilder(
  74. builder: (BuildContext context, BoxConstraints constraints) {
  75. final layout = HomeLayout(context, constraints, state.forceCollapse);
  76. const homeStack = HomeStack();
  77. final menu = _buildHomeMenu(
  78. layout: layout,
  79. context: context,
  80. state: state,
  81. );
  82. final homeMenuResizer = _buildHomeMenuResizer(context: context);
  83. final editPannel = _buildEditPannel(
  84. homeState: state,
  85. layout: layout,
  86. context: context,
  87. );
  88. const bubble = QuestionBubble();
  89. return _layoutWidgets(
  90. layout: layout,
  91. homeStack: homeStack,
  92. homeMenu: menu,
  93. editPannel: editPannel,
  94. bubble: bubble,
  95. homeMenuResizer: homeMenuResizer,
  96. );
  97. },
  98. );
  99. }
  100. Widget _buildHomeMenu({required HomeLayout layout, required BuildContext context, required HomeState state}) {
  101. final workspaceSetting = state.workspaceSetting;
  102. if (initialView == null && workspaceSetting.hasLatestView()) {
  103. initialView = workspaceSetting.latestView;
  104. final plugin = makePlugin(
  105. pluginType: initialView!.pluginType,
  106. data: initialView,
  107. );
  108. getIt<HomeStackManager>().setPlugin(plugin);
  109. }
  110. HomeMenu homeMenu = HomeMenu(
  111. user: widget.user,
  112. workspaceSetting: workspaceSetting,
  113. collapsedNotifier: getIt<HomeStackManager>().collapsedNotifier,
  114. );
  115. final latestView = workspaceSetting.hasLatestView() ? workspaceSetting.latestView : null;
  116. if (getIt<MenuSharedState>().latestOpenView == null) {
  117. /// 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.
  118. getIt<MenuSharedState>().latestOpenView = latestView;
  119. }
  120. return FocusTraversalGroup(child: RepaintBoundary(child: homeMenu));
  121. }
  122. Widget _buildEditPannel({required HomeState homeState, required BuildContext context, required HomeLayout layout}) {
  123. final homeBloc = context.read<HomeBloc>();
  124. return BlocBuilder<HomeBloc, HomeState>(
  125. buildWhen: (previous, current) => previous.pannelContext != current.pannelContext,
  126. builder: (context, state) {
  127. return state.pannelContext.fold(
  128. () => const SizedBox(),
  129. (pannelContext) => FocusTraversalGroup(
  130. child: RepaintBoundary(
  131. child: EditPannel(
  132. pannelContext: pannelContext,
  133. onEndEdit: () => homeBloc.add(const HomeEvent.dismissEditPannel()),
  134. ),
  135. ),
  136. ),
  137. );
  138. },
  139. );
  140. }
  141. Widget _buildHomeMenuResizer({
  142. required BuildContext context,
  143. }) {
  144. return MouseRegion(
  145. cursor: SystemMouseCursors.resizeLeftRight,
  146. child: GestureDetector(
  147. dragStartBehavior: DragStartBehavior.down,
  148. onPanUpdate: ((details) {
  149. context.read<HomeBloc>().add(HomeEvent.editPannelResized(details.delta.dx));
  150. }),
  151. behavior: HitTestBehavior.translucent,
  152. child: SizedBox(
  153. width: 10,
  154. height: MediaQuery.of(context).size.height,
  155. )),
  156. );
  157. }
  158. Widget _layoutWidgets({
  159. required HomeLayout layout,
  160. required Widget homeMenu,
  161. required Widget homeStack,
  162. required Widget editPannel,
  163. required Widget bubble,
  164. required Widget homeMenuResizer,
  165. }) {
  166. return Stack(
  167. children: [
  168. homeMenu
  169. .animatedPanelX(
  170. closeX: -layout.menuWidth,
  171. isClosed: !layout.showMenu,
  172. )
  173. .positioned(left: 0, top: 0, width: layout.menuWidth, bottom: 0, animate: true)
  174. .animate(layout.animDuration, Curves.easeOut),
  175. homeStack
  176. .constrained(minWidth: 500)
  177. .positioned(left: layout.homePageLOffset, right: layout.homePageROffset, bottom: 0, top: 0, animate: true)
  178. .animate(layout.animDuration, Curves.easeOut),
  179. homeMenuResizer.positioned(left: layout.homePageLOffset - 5),
  180. bubble
  181. .positioned(
  182. right: 20,
  183. bottom: 16,
  184. animate: true,
  185. )
  186. .animate(layout.animDuration, Curves.easeOut),
  187. editPannel
  188. .animatedPanelX(
  189. duration: layout.animDuration.inMilliseconds * 0.001,
  190. closeX: layout.editPannelWidth,
  191. isClosed: !layout.showEditPannel,
  192. )
  193. .positioned(right: 0, top: 0, bottom: 0, width: layout.editPannelWidth),
  194. ],
  195. );
  196. }
  197. }