app_widget.dart 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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/styled_text_button.dart';
  10. import 'package:flowy_infra_ui/style_widget/styled_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 AppWidget extends MenuItem {
  24. final App app;
  25. AppWidget(this.app, {Key? key}) : super(key: ValueKey(app.id));
  26. @override
  27. Widget build(BuildContext context) {
  28. return MultiBlocProvider(
  29. providers: [
  30. BlocProvider<AppBloc>(create: (context) {
  31. final appBloc = getIt<AppBloc>(param1: app.id);
  32. appBloc.add(const AppEvent.initial());
  33. return appBloc;
  34. }),
  35. BlocProvider<AppWatchBloc>(create: (context) {
  36. final watchBloc = getIt<AppWatchBloc>(param1: app.id);
  37. watchBloc.add(const AppWatchEvent.started());
  38. return watchBloc;
  39. }),
  40. ],
  41. child: BlocBuilder<AppWatchBloc, AppWatchState>(
  42. builder: (context, state) {
  43. final child = state.map(
  44. initial: (_) => BlocBuilder<AppBloc, AppState>(
  45. builder: (context, state) => _renderViewList(state.views),
  46. ),
  47. loadViews: (s) => _renderViewList(some(s.views)),
  48. loadFail: (s) => FlowyErrorPage(s.error.toString()),
  49. );
  50. return expandableWrapper(context, child);
  51. },
  52. ),
  53. );
  54. }
  55. ExpandableNotifier expandableWrapper(BuildContext context, Widget child) {
  56. return ExpandableNotifier(
  57. child: ScrollOnExpand(
  58. scrollOnExpand: true,
  59. scrollOnCollapse: false,
  60. child: Column(
  61. children: <Widget>[
  62. ExpandablePanel(
  63. theme: const ExpandableThemeData(
  64. headerAlignment: ExpandablePanelHeaderAlignment.center,
  65. tapBodyToExpand: false,
  66. tapBodyToCollapse: false,
  67. tapHeaderToExpand: false,
  68. iconPadding: EdgeInsets.zero,
  69. hasIcon: false,
  70. ),
  71. header: AppHeader(app),
  72. expanded: child,
  73. collapsed: const SizedBox(),
  74. ),
  75. ],
  76. ),
  77. ),
  78. );
  79. }
  80. Widget _renderViewList(Option<List<View>> views) {
  81. return Padding(
  82. padding: const EdgeInsets.symmetric(vertical: 8),
  83. child: ViewList(views),
  84. );
  85. }
  86. @override
  87. MenuItemType get type => MenuItemType.app;
  88. }
  89. class AppHeader extends StatelessWidget {
  90. final App app;
  91. const AppHeader(
  92. this.app, {
  93. Key? key,
  94. }) : super(key: key);
  95. @override
  96. Widget build(BuildContext context) {
  97. return Row(
  98. mainAxisAlignment: MainAxisAlignment.center,
  99. crossAxisAlignment: CrossAxisAlignment.center,
  100. children: [
  101. InkWell(
  102. onTap: () {
  103. ExpandableController.of(context,
  104. rebuildOnChange: false, required: true)
  105. ?.toggle();
  106. },
  107. child: ExpandableIcon(
  108. theme: ExpandableThemeData(
  109. expandIcon: Icons.arrow_drop_up,
  110. collapseIcon: Icons.arrow_drop_down,
  111. iconColor: Colors.black,
  112. iconSize: AppWidgetSize.expandedIconSize,
  113. iconPadding: EdgeInsets.zero,
  114. hasIcon: false,
  115. ),
  116. ),
  117. ),
  118. HSpace(AppWidgetSize.expandedIconRightSpace),
  119. Expanded(
  120. child: StyledTextButton(
  121. app.name,
  122. onPressed: () {
  123. debugPrint('show app document');
  124. },
  125. ),
  126. ),
  127. StyledIconButton(
  128. icon: const Icon(Icons.add),
  129. onPressed: () {
  130. debugPrint('add view');
  131. },
  132. ),
  133. ],
  134. );
  135. }
  136. // return PopupMenuButton(
  137. // iconSize: 20,
  138. // tooltip: 'create new view',
  139. // icon: const Icon(Icons.add),
  140. // padding: EdgeInsets.zero,
  141. // onSelected: (viewType) => _createView(viewType as ViewType, context),
  142. // itemBuilder: (context) => menuItemBuilder());
  143. List<PopupMenuEntry> menuItemBuilder() {
  144. return ViewType.values
  145. .where((element) => element != ViewType.Blank)
  146. .map((ty) {
  147. return PopupMenuItem<ViewType>(
  148. value: ty,
  149. child: Row(
  150. children: <Widget>[Text(ty.name)],
  151. ));
  152. }).toList();
  153. }
  154. void _createView(ViewType viewType, BuildContext context) {
  155. context.read<AppBloc>().add(AppEvent.createView("New view", "", viewType));
  156. }
  157. }