app_widget.dart 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import 'package:app_flowy/workspace/application/app/app_bloc.dart';
  2. import 'package:app_flowy/workspace/application/app/app_watch_bloc.dart';
  3. import 'package:app_flowy/workspace/presentation/app/view_list.dart';
  4. import 'package:app_flowy/workspace/presentation/widgets/menu/menu_list.dart';
  5. import 'package:app_flowy/startup/startup.dart';
  6. import 'package:expandable/expandable.dart';
  7. import 'package:flowy_infra_ui/widget/error_page.dart';
  8. import 'package:flowy_infra_ui/widget/spacing.dart';
  9. import 'package:flowy_infra_ui/style_widget/text_button.dart';
  10. import 'package:flowy_infra_ui/style_widget/icon_button.dart';
  11. import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
  12. import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
  13. import 'package:flutter/material.dart';
  14. import 'package:flutter_bloc/flutter_bloc.dart';
  15. import 'package:dartz/dartz.dart';
  16. class AppWidgetSize {
  17. static double expandedIconSize = 24;
  18. static double expandedIconRightSpace = 8;
  19. static double scale = 1;
  20. static double get expandedPadding =>
  21. expandedIconSize * scale + expandedIconRightSpace;
  22. }
  23. class AppWidgetContext {
  24. final App app;
  25. AppWidgetContext(this.app);
  26. Key valueKey() => ValueKey("${app.id}${app.version}");
  27. }
  28. class AppWidget extends MenuItem {
  29. final AppWidgetContext appCtx;
  30. AppWidget(this.appCtx, {Key? key}) : super(key: appCtx.valueKey());
  31. @override
  32. Widget build(BuildContext context) {
  33. return MultiBlocProvider(
  34. providers: [
  35. BlocProvider<AppBloc>(create: (context) {
  36. final appBloc = getIt<AppBloc>(param1: appCtx.app.id);
  37. appBloc.add(const AppEvent.initial());
  38. return appBloc;
  39. }),
  40. BlocProvider<AppWatchBloc>(create: (context) {
  41. final watchBloc = getIt<AppWatchBloc>(param1: appCtx.app.id);
  42. watchBloc.add(const AppWatchEvent.started());
  43. return watchBloc;
  44. }),
  45. ],
  46. child: BlocBuilder<AppWatchBloc, AppWatchState>(
  47. builder: (context, state) {
  48. final child = state.map(
  49. initial: (_) => BlocBuilder<AppBloc, AppState>(
  50. builder: (context, state) => _renderViewList(state.views),
  51. ),
  52. loadViews: (s) => _renderViewList(some(s.views)),
  53. loadFail: (s) => FlowyErrorPage(s.error.toString()),
  54. );
  55. return expandableWrapper(context, child);
  56. },
  57. ),
  58. );
  59. }
  60. ExpandableNotifier expandableWrapper(BuildContext context, Widget child) {
  61. return ExpandableNotifier(
  62. child: ScrollOnExpand(
  63. scrollOnExpand: true,
  64. scrollOnCollapse: false,
  65. child: Column(
  66. children: <Widget>[
  67. ExpandablePanel(
  68. theme: const ExpandableThemeData(
  69. headerAlignment: ExpandablePanelHeaderAlignment.center,
  70. tapBodyToExpand: false,
  71. tapBodyToCollapse: false,
  72. tapHeaderToExpand: false,
  73. iconPadding: EdgeInsets.zero,
  74. hasIcon: false,
  75. ),
  76. header: AppHeader(appCtx.app),
  77. expanded: child,
  78. collapsed: const SizedBox(),
  79. ),
  80. ],
  81. ),
  82. ),
  83. );
  84. }
  85. Widget _renderViewList(Option<List<View>> some) {
  86. List<View> views = some.fold(
  87. () => List.empty(growable: true),
  88. (views) => views,
  89. );
  90. return Padding(
  91. padding: const EdgeInsets.symmetric(vertical: 8),
  92. child: ViewList(views));
  93. }
  94. @override
  95. MenuItemType get type => MenuItemType.app;
  96. }
  97. class AppHeader extends StatelessWidget {
  98. final App app;
  99. const AppHeader(
  100. this.app, {
  101. Key? key,
  102. }) : super(key: key);
  103. @override
  104. Widget build(BuildContext context) {
  105. return Row(
  106. mainAxisAlignment: MainAxisAlignment.center,
  107. crossAxisAlignment: CrossAxisAlignment.center,
  108. children: [
  109. InkWell(
  110. onTap: () {
  111. ExpandableController.of(context,
  112. rebuildOnChange: false, required: true)
  113. ?.toggle();
  114. },
  115. child: ExpandableIcon(
  116. theme: ExpandableThemeData(
  117. expandIcon: Icons.arrow_drop_up,
  118. collapseIcon: Icons.arrow_drop_down,
  119. iconColor: Colors.black,
  120. iconSize: AppWidgetSize.expandedIconSize,
  121. iconPadding: EdgeInsets.zero,
  122. hasIcon: false,
  123. ),
  124. ),
  125. ),
  126. HSpace(AppWidgetSize.expandedIconRightSpace),
  127. Expanded(
  128. child: FlowyTextButton(
  129. app.name,
  130. onPressed: () {
  131. debugPrint('show app document');
  132. },
  133. ),
  134. ),
  135. // StyledIconButton(
  136. // icon: const Icon(Icons.add),
  137. // onPressed: () {
  138. // debugPrint('add view');
  139. // },
  140. // ),
  141. PopupMenuButton(
  142. iconSize: 20,
  143. tooltip: 'create new view',
  144. icon: const Icon(Icons.add),
  145. padding: EdgeInsets.zero,
  146. onSelected: (viewType) =>
  147. _createView(viewType as ViewType, context),
  148. itemBuilder: (context) => menuItemBuilder())
  149. ],
  150. );
  151. }
  152. List<PopupMenuEntry> menuItemBuilder() {
  153. return ViewType.values
  154. .where((element) => element != ViewType.Blank)
  155. .map((ty) {
  156. return PopupMenuItem<ViewType>(
  157. value: ty,
  158. child: Row(
  159. children: <Widget>[Text(ty.name)],
  160. ));
  161. }).toList();
  162. }
  163. void _createView(ViewType viewType, BuildContext context) {
  164. context.read<AppBloc>().add(AppEvent.createView("New view", "", viewType));
  165. }
  166. }