| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 | import 'package:app_flowy/workspace/presentation/widgets/menu/widget/top_bar.dart';import 'package:flowy_infra/notifier.dart';import 'package:flowy_infra/size.dart';import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';import 'package:flowy_infra_ui/widget/spacing.dart';import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';import 'package:flowy_sdk/protobuf/flowy-workspace-infra/view_create.pb.dart';import 'package:flowy_sdk/protobuf/flowy-workspace-infra/workspace_setting.pb.dart';import 'package:flutter/material.dart';import 'package:flutter_bloc/flutter_bloc.dart';import 'package:provider/provider.dart';import 'package:styled_widget/styled_widget.dart';import 'package:expandable/expandable.dart';import 'package:flowy_infra/time/duration.dart';import 'package:app_flowy/startup/startup.dart';import 'package:app_flowy/workspace/application/menu/menu_bloc.dart';import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';import 'package:app_flowy/workspace/presentation/widgets/menu/widget/menu_user.dart';import 'widget/app/menu_app.dart';import 'widget/app/create_button.dart';import 'widget/menu_trash.dart';// [[diagram: HomeMenu's widget structure]]//                                                                                    get user profile or modify user//                                                                                   ┌──────┐//                 ┌──────────┐                                                  ┌──▶│IUser │//              ┌─▶│MenuTopBar│                     ┌────────┐  ┌─────────────┐  │   └──────┘//              │  └──────────┘                 ┌───│MenuUser│─▶│MenuUserBloc │──┤// ┌──────────┐ │                               │   └────────┘  └─────────────┘  │   ┌─────────────┐// │ HomeMenu │─┤                               │                                └──▶│IUserListener│// └──────────┘ │                               │                                    └─────────────┘//              │                               │                                    listen workspace changes or user//              │                         impl  │                                    profile changes//              │  ┌──────────┐    ┌─────────┐  │//              └─▶│ MenuList │───▶│MenuItem │◀─┤//                 └──────────┘    └─────────┘  │                  ┌────────┐//                                              │               ┌─▶│AppBloc │  fetch app's views or modify view//                                              │               │  └────────┘//                                              │   ┌────────┐  │//                                              └───│MenuApp │──┤//                                                  └────────┘class HomeMenu extends StatelessWidget {  final PublishNotifier<bool> _collapsedNotifier;  final UserProfile user;  final CurrentWorkspaceSetting workspaceSetting;  const HomeMenu({    Key? key,    required this.user,    required this.workspaceSetting,    required PublishNotifier<bool> collapsedNotifier,  })  : _collapsedNotifier = collapsedNotifier,        super(key: key);  @override  Widget build(BuildContext context) {    return MultiBlocProvider(      providers: [        BlocProvider<MenuBloc>(          create: (context) {            final menuBloc = getIt<MenuBloc>(param1: user, param2: workspaceSetting.workspace.id);            menuBloc.add(const MenuEvent.initial());            return menuBloc;          },        ),      ],      child: MultiBlocListener(        listeners: [          BlocListener<MenuBloc, MenuState>(            listenWhen: (p, c) => p.stackContext != c.stackContext,            listener: (context, state) {              getIt<HomeStackManager>().setStack(state.stackContext);            },          ),          BlocListener<MenuBloc, MenuState>(            listenWhen: (p, c) => p.isCollapse != c.isCollapse,            listener: (context, state) => _collapsedNotifier.value = state.isCollapse,          )        ],        child: BlocBuilder<MenuBloc, MenuState>(          builder: (context, state) => _renderBody(context),        ),      ),    );  }  Widget _renderBody(BuildContext context) {    // nested cloumn: https://siddharthmolleti.com/flutter-box-constraints-nested-column-s-row-s-3dfacada7361    return Container(      color: Theme.of(context).colorScheme.background,      child: ChangeNotifierProvider(        create: (_) => MenuSharedState(view: workspaceSetting.hasLatestView() ? workspaceSetting.latestView : null),        child: Consumer(builder: (context, MenuSharedState sharedState, child) {          return Column(            mainAxisAlignment: MainAxisAlignment.start,            children: [              Expanded(                child: Column(                  mainAxisAlignment: MainAxisAlignment.start,                  children: [                    const MenuTopBar(),                    const VSpace(10),                    _renderApps(context),                  ],                ).padding(horizontal: Insets.l),              ),              const VSpace(20),              _renderTrash(context).padding(horizontal: Insets.l),              const VSpace(20),              _renderNewAppButton(context),            ],          );        }),      ),    );  }  Widget _renderApps(BuildContext context) {    return ExpandableTheme(      data: ExpandableThemeData(useInkWell: true, animationDuration: Durations.medium),      child: Expanded(        child: ScrollConfiguration(          behavior: const ScrollBehavior().copyWith(scrollbars: false),          child: BlocSelector<MenuBloc, MenuState, List<Widget>>(            selector: (state) {              List<Widget> menuItems = [];              menuItems.add(MenuUser(user));              List<MenuApp> appWidgets =                  state.apps.foldRight([], (apps, _) => apps.map((app) => MenuApp(app)).toList());              menuItems.addAll(appWidgets);              return menuItems;            },            builder: (context, menuItems) => ListView.separated(              itemCount: menuItems.length,              separatorBuilder: (context, index) {                if (index == 0) {                  return const VSpace(20);                } else {                  return VSpace(MenuAppSizes.appVPadding);                }              },              physics: StyledScrollPhysics(),              itemBuilder: (BuildContext context, int index) {                return menuItems[index];              },            ),          ),        ),      ),    );  }  Widget _renderTrash(BuildContext context) {    return const MenuTrash();  }  Widget _renderNewAppButton(BuildContext context) {    return NewAppButton(      press: (appName) => context.read<MenuBloc>().add(MenuEvent.createApp(appName, desc: "")),    );  }}class MenuSharedState extends ChangeNotifier {  PublishNotifier<View> forcedOpenView = PublishNotifier();  ValueNotifier<View?> selectedView = ValueNotifier<View?>(null);  MenuSharedState({View? view}) {    if (view != null) {      selectedView.value = view;    }    forcedOpenView.addPublishListener((view) {      selectedView.value = view;    });  }}
 |