123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- 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/theme.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-data-model/protobuf.dart' show UserProfile;
- import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
- import 'package:flowy_sdk/protobuf/flowy-folder-data-model/workspace.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 column: https://siddharthmolleti.com/flutter-box-constraints-nested-column-s-row-s-3dfacada7361
- final theme = context.watch<AppTheme>();
- return Container(
- color: theme.bg1,
- 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;
- });
- }
- }
|