Sfoglia il codice sorgente

[flutter]: open the view automatically after create it

appflowy 3 anni fa
parent
commit
941c99ecd1

+ 12 - 4
app_flowy/lib/workspace/application/app/app_bloc.dart

@@ -25,10 +25,16 @@ class AppBloc extends Bloc<AppEvent, AppState> {
       },
       createView: (CreateView value) async* {
         final viewOrFailed = await appManager.createView(name: value.name, desc: value.desc, viewType: value.viewType);
-        yield viewOrFailed.fold((view) => state, (error) {
-          Log.error(error);
-          return state.copyWith(successOrFailure: right(error));
-        });
+        yield viewOrFailed.fold(
+          (view) => state.copyWith(
+            selectedView: view,
+            successOrFailure: left(unit),
+          ),
+          (error) {
+            Log.error(error);
+            return state.copyWith(successOrFailure: right(error));
+          },
+        );
       },
       didReceiveViews: (e) async* {
         yield state.copyWith(views: e.views);
@@ -75,12 +81,14 @@ class AppState with _$AppState {
   const factory AppState({
     required bool isLoading,
     required List<View>? views,
+    View? selectedView,
     required Either<Unit, WorkspaceError> successOrFailure,
   }) = _AppState;
 
   factory AppState.initial() => AppState(
         isLoading: false,
         views: null,
+        selectedView: null,
         successOrFailure: left(unit),
       );
 }

+ 26 - 1
app_flowy/lib/workspace/application/app/app_bloc.freezed.dart

@@ -447,10 +447,12 @@ class _$AppStateTearOff {
   _AppState call(
       {required bool isLoading,
       required List<View>? views,
+      View? selectedView,
       required Either<Unit, WorkspaceError> successOrFailure}) {
     return _AppState(
       isLoading: isLoading,
       views: views,
+      selectedView: selectedView,
       successOrFailure: successOrFailure,
     );
   }
@@ -463,6 +465,7 @@ const $AppState = _$AppStateTearOff();
 mixin _$AppState {
   bool get isLoading => throw _privateConstructorUsedError;
   List<View>? get views => throw _privateConstructorUsedError;
+  View? get selectedView => throw _privateConstructorUsedError;
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;
 
@@ -478,6 +481,7 @@ abstract class $AppStateCopyWith<$Res> {
   $Res call(
       {bool isLoading,
       List<View>? views,
+      View? selectedView,
       Either<Unit, WorkspaceError> successOrFailure});
 }
 
@@ -493,6 +497,7 @@ class _$AppStateCopyWithImpl<$Res> implements $AppStateCopyWith<$Res> {
   $Res call({
     Object? isLoading = freezed,
     Object? views = freezed,
+    Object? selectedView = freezed,
     Object? successOrFailure = freezed,
   }) {
     return _then(_value.copyWith(
@@ -504,6 +509,10 @@ class _$AppStateCopyWithImpl<$Res> implements $AppStateCopyWith<$Res> {
           ? _value.views
           : views // ignore: cast_nullable_to_non_nullable
               as List<View>?,
+      selectedView: selectedView == freezed
+          ? _value.selectedView
+          : selectedView // ignore: cast_nullable_to_non_nullable
+              as View?,
       successOrFailure: successOrFailure == freezed
           ? _value.successOrFailure
           : successOrFailure // ignore: cast_nullable_to_non_nullable
@@ -520,6 +529,7 @@ abstract class _$AppStateCopyWith<$Res> implements $AppStateCopyWith<$Res> {
   $Res call(
       {bool isLoading,
       List<View>? views,
+      View? selectedView,
       Either<Unit, WorkspaceError> successOrFailure});
 }
 
@@ -536,6 +546,7 @@ class __$AppStateCopyWithImpl<$Res> extends _$AppStateCopyWithImpl<$Res>
   $Res call({
     Object? isLoading = freezed,
     Object? views = freezed,
+    Object? selectedView = freezed,
     Object? successOrFailure = freezed,
   }) {
     return _then(_AppState(
@@ -547,6 +558,10 @@ class __$AppStateCopyWithImpl<$Res> extends _$AppStateCopyWithImpl<$Res>
           ? _value.views
           : views // ignore: cast_nullable_to_non_nullable
               as List<View>?,
+      selectedView: selectedView == freezed
+          ? _value.selectedView
+          : selectedView // ignore: cast_nullable_to_non_nullable
+              as View?,
       successOrFailure: successOrFailure == freezed
           ? _value.successOrFailure
           : successOrFailure // ignore: cast_nullable_to_non_nullable
@@ -561,6 +576,7 @@ class _$_AppState implements _AppState {
   const _$_AppState(
       {required this.isLoading,
       required this.views,
+      this.selectedView,
       required this.successOrFailure});
 
   @override
@@ -568,11 +584,13 @@ class _$_AppState implements _AppState {
   @override
   final List<View>? views;
   @override
+  final View? selectedView;
+  @override
   final Either<Unit, WorkspaceError> successOrFailure;
 
   @override
   String toString() {
-    return 'AppState(isLoading: $isLoading, views: $views, successOrFailure: $successOrFailure)';
+    return 'AppState(isLoading: $isLoading, views: $views, selectedView: $selectedView, successOrFailure: $successOrFailure)';
   }
 
   @override
@@ -584,6 +602,9 @@ class _$_AppState implements _AppState {
                     .equals(other.isLoading, isLoading)) &&
             (identical(other.views, views) ||
                 const DeepCollectionEquality().equals(other.views, views)) &&
+            (identical(other.selectedView, selectedView) ||
+                const DeepCollectionEquality()
+                    .equals(other.selectedView, selectedView)) &&
             (identical(other.successOrFailure, successOrFailure) ||
                 const DeepCollectionEquality()
                     .equals(other.successOrFailure, successOrFailure)));
@@ -594,6 +615,7 @@ class _$_AppState implements _AppState {
       runtimeType.hashCode ^
       const DeepCollectionEquality().hash(isLoading) ^
       const DeepCollectionEquality().hash(views) ^
+      const DeepCollectionEquality().hash(selectedView) ^
       const DeepCollectionEquality().hash(successOrFailure);
 
   @JsonKey(ignore: true)
@@ -606,6 +628,7 @@ abstract class _AppState implements AppState {
   const factory _AppState(
       {required bool isLoading,
       required List<View>? views,
+      View? selectedView,
       required Either<Unit, WorkspaceError> successOrFailure}) = _$_AppState;
 
   @override
@@ -613,6 +636,8 @@ abstract class _AppState implements AppState {
   @override
   List<View>? get views => throw _privateConstructorUsedError;
   @override
+  View? get selectedView => throw _privateConstructorUsedError;
+  @override
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;
   @override

+ 27 - 27
app_flowy/lib/workspace/application/menu/menu_bloc.freezed.dart

@@ -24,7 +24,7 @@ class _$MenuEventTearOff {
     return const Collapse();
   }
 
-  OpenPage openPage(HomeStackContext context) {
+  OpenPage openPage(HomeStackContext<dynamic> context) {
     return OpenPage(
       context,
     );
@@ -53,7 +53,7 @@ mixin _$MenuEvent {
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
     required TResult Function() collapse,
-    required TResult Function(HomeStackContext context) openPage,
+    required TResult Function(HomeStackContext<dynamic> context) openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
         didReceiveApps,
@@ -63,7 +63,7 @@ mixin _$MenuEvent {
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function()? collapse,
-    TResult Function(HomeStackContext context)? openPage,
+    TResult Function(HomeStackContext<dynamic> context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
         didReceiveApps,
@@ -145,7 +145,7 @@ class _$_Initial implements _Initial {
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
     required TResult Function() collapse,
-    required TResult Function(HomeStackContext context) openPage,
+    required TResult Function(HomeStackContext<dynamic> context) openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
         didReceiveApps,
@@ -158,7 +158,7 @@ class _$_Initial implements _Initial {
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function()? collapse,
-    TResult Function(HomeStackContext context)? openPage,
+    TResult Function(HomeStackContext<dynamic> context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
         didReceiveApps,
@@ -242,7 +242,7 @@ class _$Collapse implements Collapse {
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
     required TResult Function() collapse,
-    required TResult Function(HomeStackContext context) openPage,
+    required TResult Function(HomeStackContext<dynamic> context) openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
         didReceiveApps,
@@ -255,7 +255,7 @@ class _$Collapse implements Collapse {
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function()? collapse,
-    TResult Function(HomeStackContext context)? openPage,
+    TResult Function(HomeStackContext<dynamic> context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
         didReceiveApps,
@@ -304,7 +304,7 @@ abstract class Collapse implements MenuEvent {
 abstract class $OpenPageCopyWith<$Res> {
   factory $OpenPageCopyWith(OpenPage value, $Res Function(OpenPage) then) =
       _$OpenPageCopyWithImpl<$Res>;
-  $Res call({HomeStackContext context});
+  $Res call({HomeStackContext<dynamic> context});
 }
 
 /// @nodoc
@@ -324,7 +324,7 @@ class _$OpenPageCopyWithImpl<$Res> extends _$MenuEventCopyWithImpl<$Res>
       context == freezed
           ? _value.context
           : context // ignore: cast_nullable_to_non_nullable
-              as HomeStackContext,
+              as HomeStackContext<dynamic>,
     ));
   }
 }
@@ -335,7 +335,7 @@ class _$OpenPage implements OpenPage {
   const _$OpenPage(this.context);
 
   @override
-  final HomeStackContext context;
+  final HomeStackContext<dynamic> context;
 
   @override
   String toString() {
@@ -364,7 +364,7 @@ class _$OpenPage implements OpenPage {
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
     required TResult Function() collapse,
-    required TResult Function(HomeStackContext context) openPage,
+    required TResult Function(HomeStackContext<dynamic> context) openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
         didReceiveApps,
@@ -377,7 +377,7 @@ class _$OpenPage implements OpenPage {
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function()? collapse,
-    TResult Function(HomeStackContext context)? openPage,
+    TResult Function(HomeStackContext<dynamic> context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
         didReceiveApps,
@@ -419,9 +419,9 @@ class _$OpenPage implements OpenPage {
 }
 
 abstract class OpenPage implements MenuEvent {
-  const factory OpenPage(HomeStackContext context) = _$OpenPage;
+  const factory OpenPage(HomeStackContext<dynamic> context) = _$OpenPage;
 
-  HomeStackContext get context => throw _privateConstructorUsedError;
+  HomeStackContext<dynamic> get context => throw _privateConstructorUsedError;
   @JsonKey(ignore: true)
   $OpenPageCopyWith<OpenPage> get copyWith =>
       throw _privateConstructorUsedError;
@@ -502,7 +502,7 @@ class _$CreateApp implements CreateApp {
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
     required TResult Function() collapse,
-    required TResult Function(HomeStackContext context) openPage,
+    required TResult Function(HomeStackContext<dynamic> context) openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
         didReceiveApps,
@@ -515,7 +515,7 @@ class _$CreateApp implements CreateApp {
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function()? collapse,
-    TResult Function(HomeStackContext context)? openPage,
+    TResult Function(HomeStackContext<dynamic> context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
         didReceiveApps,
@@ -633,7 +633,7 @@ class _$ReceiveApps implements ReceiveApps {
   TResult when<TResult extends Object?>({
     required TResult Function() initial,
     required TResult Function() collapse,
-    required TResult Function(HomeStackContext context) openPage,
+    required TResult Function(HomeStackContext<dynamic> context) openPage,
     required TResult Function(String name, String? desc) createApp,
     required TResult Function(Either<List<App>, WorkspaceError> appsOrFail)
         didReceiveApps,
@@ -646,7 +646,7 @@ class _$ReceiveApps implements ReceiveApps {
   TResult maybeWhen<TResult extends Object?>({
     TResult Function()? initial,
     TResult Function()? collapse,
-    TResult Function(HomeStackContext context)? openPage,
+    TResult Function(HomeStackContext<dynamic> context)? openPage,
     TResult Function(String name, String? desc)? createApp,
     TResult Function(Either<List<App>, WorkspaceError> appsOrFail)?
         didReceiveApps,
@@ -706,7 +706,7 @@ class _$MenuStateTearOff {
       {required bool isCollapse,
       required Option<List<App>> apps,
       required Either<Unit, WorkspaceError> successOrFailure,
-      required HomeStackContext context}) {
+      required HomeStackContext<dynamic> context}) {
     return _MenuState(
       isCollapse: isCollapse,
       apps: apps,
@@ -725,7 +725,7 @@ mixin _$MenuState {
   Option<List<App>> get apps => throw _privateConstructorUsedError;
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;
-  HomeStackContext get context => throw _privateConstructorUsedError;
+  HomeStackContext<dynamic> get context => throw _privateConstructorUsedError;
 
   @JsonKey(ignore: true)
   $MenuStateCopyWith<MenuState> get copyWith =>
@@ -740,7 +740,7 @@ abstract class $MenuStateCopyWith<$Res> {
       {bool isCollapse,
       Option<List<App>> apps,
       Either<Unit, WorkspaceError> successOrFailure,
-      HomeStackContext context});
+      HomeStackContext<dynamic> context});
 }
 
 /// @nodoc
@@ -774,7 +774,7 @@ class _$MenuStateCopyWithImpl<$Res> implements $MenuStateCopyWith<$Res> {
       context: context == freezed
           ? _value.context
           : context // ignore: cast_nullable_to_non_nullable
-              as HomeStackContext,
+              as HomeStackContext<dynamic>,
     ));
   }
 }
@@ -789,7 +789,7 @@ abstract class _$MenuStateCopyWith<$Res> implements $MenuStateCopyWith<$Res> {
       {bool isCollapse,
       Option<List<App>> apps,
       Either<Unit, WorkspaceError> successOrFailure,
-      HomeStackContext context});
+      HomeStackContext<dynamic> context});
 }
 
 /// @nodoc
@@ -824,7 +824,7 @@ class __$MenuStateCopyWithImpl<$Res> extends _$MenuStateCopyWithImpl<$Res>
       context: context == freezed
           ? _value.context
           : context // ignore: cast_nullable_to_non_nullable
-              as HomeStackContext,
+              as HomeStackContext<dynamic>,
     ));
   }
 }
@@ -845,7 +845,7 @@ class _$_MenuState implements _MenuState {
   @override
   final Either<Unit, WorkspaceError> successOrFailure;
   @override
-  final HomeStackContext context;
+  final HomeStackContext<dynamic> context;
 
   @override
   String toString() {
@@ -887,7 +887,7 @@ abstract class _MenuState implements MenuState {
       {required bool isCollapse,
       required Option<List<App>> apps,
       required Either<Unit, WorkspaceError> successOrFailure,
-      required HomeStackContext context}) = _$_MenuState;
+      required HomeStackContext<dynamic> context}) = _$_MenuState;
 
   @override
   bool get isCollapse => throw _privateConstructorUsedError;
@@ -897,7 +897,7 @@ abstract class _MenuState implements MenuState {
   Either<Unit, WorkspaceError> get successOrFailure =>
       throw _privateConstructorUsedError;
   @override
-  HomeStackContext get context => throw _privateConstructorUsedError;
+  HomeStackContext<dynamic> get context => throw _privateConstructorUsedError;
   @override
   @JsonKey(ignore: true)
   _$MenuStateCopyWith<_MenuState> get copyWith =>

+ 0 - 1
app_flowy/lib/workspace/domain/page_stack/page_stack.dart

@@ -1,4 +1,3 @@
-import 'package:equatable/equatable.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 import 'package:app_flowy/startup/startup.dart';

+ 2 - 1
app_flowy/lib/workspace/presentation/stack_page/trash/widget/sizes.dart

@@ -5,5 +5,6 @@ class TrashSizes {
   static double get lashModifyWidth => 230 * scale;
   static double get createTimeWidth => 230 * scale;
   static double get padding => 100 * scale;
-  static double get totalWidth => TrashSizes.fileNameWidth + TrashSizes.lashModifyWidth + TrashSizes.createTimeWidth;
+  static double get totalWidth =>
+      TrashSizes.fileNameWidth + TrashSizes.lashModifyWidth + TrashSizes.createTimeWidth + TrashSizes.padding;
 }

+ 1 - 1
app_flowy/lib/workspace/presentation/widgets/dialogs.dart

@@ -54,7 +54,7 @@ class _CreateTextFieldDialog extends State<TextFieldDialog> {
           ],
           FlowyFormTextInput(
             hintText: widget.value,
-            textStyle: const TextStyle(fontSize: 28, fontWeight: FontWeight.w400),
+            textStyle: const TextStyle(fontSize: 24, fontWeight: FontWeight.w400),
             autoFocus: true,
             onChanged: (text) {
               newValue = text;

+ 0 - 1
app_flowy/lib/workspace/presentation/widgets/menu/menu.dart

@@ -2,7 +2,6 @@ import 'package:app_flowy/workspace/presentation/widgets/menu/widget/top_bar.dar
 import 'package:dartz/dartz.dart';
 import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
-import 'package:flowy_infra_ui/widget/error_page.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/app_create.pb.dart';

+ 6 - 65
app_flowy/lib/workspace/presentation/widgets/menu/widget/app/create_button.dart

@@ -3,19 +3,10 @@ import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
 import 'package:flowy_infra/image.dart';
 import 'package:flowy_infra/size.dart';
 import 'package:flowy_infra_ui/style_widget/button.dart';
-import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flutter/material.dart';
-import 'package:styled_widget/styled_widget.dart';
 import 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
 import 'package:flowy_infra_ui/style_widget/extension.dart';
-import 'package:app_flowy/startup/tasks/application_task.dart';
-import 'package:flowy_infra/text_style.dart';
-import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/style_widget/text_input.dart';
-import 'package:flowy_infra_ui/widget/spacing.dart';
 // ignore: implementation_imports
-import 'package:provider/src/provider.dart';
-import 'package:textstyle_extensions/textstyle_extensions.dart';
 
 class NewAppButton extends StatelessWidget {
   final Function(String)? press;
@@ -41,64 +32,14 @@ class NewAppButton extends StatelessWidget {
   }
 
   Future<void> _showCreateAppDialog(BuildContext context) async {
-    await CreateAppDialog(
-      confirm: (appName) {
-        if (appName.isNotEmpty && press != null) {
-          press!(appName);
+    return TextFieldDialog(
+      title: 'New App',
+      value: "",
+      confirm: (newValue) {
+        if (newValue.isNotEmpty && press != null) {
+          press!(newValue);
         }
       },
     ).show(context);
   }
 }
-
-class CreateAppDialog extends StatefulWidget {
-  final void Function()? cancel;
-  final void Function(String) confirm;
-
-  const CreateAppDialog({required this.confirm, this.cancel, Key? key}) : super(key: key);
-
-  @override
-  State<CreateAppDialog> createState() => _CreateAppDialogState();
-}
-
-class _CreateAppDialogState extends State<CreateAppDialog> {
-  String appName = "";
-
-  @override
-  Widget build(BuildContext context) {
-    final theme = context.watch<AppTheme>();
-    return StyledDialog(
-      child: Column(
-        crossAxisAlignment: CrossAxisAlignment.start,
-        children: <Widget>[
-          ...[
-            Text('Create App'.toUpperCase(), style: TextStyles.T1.textColor(theme.shader4)),
-            VSpace(Insets.sm * 1.5),
-            // Container(color: theme.greyWeak.withOpacity(.35), height: 1),
-            VSpace(Insets.m * 1.5),
-          ],
-          FlowyFormTextInput(
-            hintText: "App name",
-            onChanged: (text) {
-              appName = text;
-            },
-          ),
-          SizedBox(height: Insets.l),
-          SizedBox(
-            height: 40,
-            child: OkCancelButton(
-              onOkPressed: () {
-                widget.confirm(appName);
-              },
-              onCancelPressed: () {
-                if (widget.cancel != null) {
-                  widget.cancel!();
-                }
-              },
-            ),
-          )
-        ],
-      ),
-    );
-  }
-}

+ 56 - 29
app_flowy/lib/workspace/presentation/widgets/menu/widget/app/menu_app.dart

@@ -1,6 +1,7 @@
 import 'package:app_flowy/workspace/presentation/widgets/menu/widget/app/header.dart';
 import 'package:expandable/expandable.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:app_flowy/startup/startup.dart';
@@ -9,23 +10,6 @@ import 'package:app_flowy/workspace/presentation/widgets/menu/menu.dart';
 import 'package:provider/provider.dart';
 import 'package:styled_widget/styled_widget.dart';
 import 'section/section.dart';
-
-class MenuAppSizes {
-  static double expandedIconSize = 16;
-  static double expandedIconPadding = 6;
-  static double scale = 1;
-  static double get expandedPadding => expandedIconSize * scale + expandedIconPadding;
-}
-
-class MenuAppContext {
-  final App app;
-  final viewList = ViewListNotifier();
-
-  MenuAppContext(this.app);
-
-  Key valueKey() => ValueKey("${app.id}${app.version}");
-}
-
 // [[diagram: MenuApp]]
 //                     ┌────────┐
 //               ┌────▶│AppBloc │────────────────┐
@@ -61,18 +45,25 @@ class MenuApp extends MenuItem {
   Widget build(BuildContext context) {
     return MultiBlocProvider(
       providers: [
-        BlocProvider<AppBloc>(create: (context) {
-          final appBloc = getIt<AppBloc>(param1: appCtx.app.id);
-          appBloc.add(const AppEvent.initial());
-          return appBloc;
-        }),
+        BlocProvider<AppBloc>(
+          create: (context) {
+            final appBloc = getIt<AppBloc>(param1: appCtx.app.id);
+            appBloc.add(const AppEvent.initial());
+            return appBloc;
+          },
+        ),
       ],
-      child: BlocBuilder<AppBloc, AppState>(
-        builder: (context, state) {
-          appCtx.viewList.items = state.views ?? List.empty(growable: false);
-          final child = _renderViewSection(appCtx.viewList);
-          return expandableWrapper(context, child);
-        },
+      child: BlocListener<AppBloc, AppState>(
+        listenWhen: (p, c) => p.selectedView != c.selectedView,
+        listener: (context, state) => appCtx.viewList.selectView = state.selectedView,
+        child: BlocBuilder<AppBloc, AppState>(
+          buildWhen: (p, c) => p.views != c.views,
+          builder: (context, state) {
+            appCtx.viewList.views = state.views;
+            final child = _renderViewSection(appCtx.viewList);
+            return expandableWrapper(context, child);
+          },
+        ),
       ),
     );
   }
@@ -109,7 +100,7 @@ class MenuApp extends MenuItem {
         ChangeNotifierProvider.value(value: viewListNotifier),
       ],
       child: Consumer(builder: (context, ViewListNotifier notifier, child) {
-        return ViewSection(notifier.items).padding(vertical: 8);
+        return const ViewSection().padding(vertical: 8);
       }),
     );
   }
@@ -117,3 +108,39 @@ class MenuApp extends MenuItem {
   @override
   MenuItemType get type => MenuItemType.app;
 }
+
+class MenuAppSizes {
+  static double expandedIconSize = 16;
+  static double expandedIconPadding = 6;
+  static double scale = 1;
+  static double get expandedPadding => expandedIconSize * scale + expandedIconPadding;
+}
+
+class ViewListNotifier extends ChangeNotifier {
+  List<View> _views = [];
+  View? _selectedView;
+  ViewListNotifier();
+
+  set views(List<View>? items) {
+    _views = items ?? List.empty(growable: false);
+    notifyListeners();
+  }
+
+  set selectView(View? view) {
+    _selectedView = view;
+    notifyListeners();
+  }
+
+  get selectedView => _selectedView;
+
+  List<View> get views => _views;
+}
+
+class MenuAppContext {
+  final App app;
+  final viewList = ViewListNotifier();
+
+  MenuAppContext(this.app);
+
+  Key valueKey() => ValueKey("${app.id}${app.version}");
+}

+ 1 - 4
app_flowy/lib/workspace/presentation/widgets/menu/widget/app/section/item.dart

@@ -45,10 +45,7 @@ class ViewSectionItem extends StatelessWidget {
       child: BlocBuilder<ViewBloc, ViewState>(
         builder: (context, state) {
           return InkWell(
-            onTap: () {
-              onSelected(context.read<ViewBloc>().state.view);
-              getIt<HomeStackManager>().setStack(state.view.stackContext());
-            },
+            onTap: () => onSelected(context.read<ViewBloc>().state.view),
             child: FlowyHover(
               config: HoverDisplayConfig(hoverColor: theme.bg3),
               builder: (_, onHover) => _render(context, onHover, state),

+ 46 - 21
app_flowy/lib/workspace/presentation/widgets/menu/widget/app/section/section.dart

@@ -1,3 +1,7 @@
+import 'package:app_flowy/startup/startup.dart';
+import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
+import 'package:app_flowy/workspace/domain/view_ext.dart';
+import 'package:app_flowy/workspace/presentation/widgets/menu/widget/app/menu_app.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
@@ -5,50 +9,69 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:provider/provider.dart';
 import 'package:styled_widget/styled_widget.dart';
 import 'item.dart';
-
-class ViewListNotifier extends ChangeNotifier {
-  List<View>? views;
-  ViewListNotifier();
-
-  set items(List<View> items) {
-    views = items;
-    notifyListeners();
-  }
-
-  List<View> get items => views ?? [];
-}
+import 'package:async/async.dart';
 
 class ViewSectionNotifier with ChangeNotifier {
   List<View> innerViews;
   View? _selectedView;
+  CancelableOperation? _notifyListenerOperation;
   ViewSectionNotifier(this.innerViews);
 
   set views(List<View> views) => innerViews = views;
   List<View> get views => innerViews;
+  set setViews(List<View> views) {
+    if (innerViews != views) {
+      innerViews = views;
+      _notifyListeners();
+    }
+  }
 
-  void setSelectedView(View view) {
-    _selectedView = view;
-    notifyListeners();
+  set selectView(View? view) {
+    if (_selectedView == view) {
+      return;
+    }
+
+    if (view != null) {
+      _selectedView = view;
+
+      WidgetsBinding.instance?.addPostFrameCallback((_) {
+        getIt<HomeStackManager>().setStack(view.stackContext());
+      });
+
+      _notifyListeners();
+    } else {
+      // WidgetsBinding.instance?.addPostFrameCallback((_) {
+      //   getIt<HomeStackManager>().setStack(BlankStackContext());
+      // });
+    }
   }
 
   View? get selectedView => _selectedView;
 
   void update(ViewListNotifier notifier) {
-    innerViews = notifier.items;
-    notifyListeners();
+    setViews = notifier.views;
+    selectView = notifier.selectedView;
+  }
+
+  void _notifyListeners() {
+    _notifyListenerOperation?.cancel();
+    _notifyListenerOperation = CancelableOperation.fromFuture(
+      Future.delayed(const Duration(milliseconds: 30), () {}),
+    ).then((_) {
+      notifyListeners();
+    });
   }
 }
 
 class ViewSection extends StatelessWidget {
-  final List<View> views;
-  const ViewSection(this.views, {Key? key}) : super(key: key);
+  const ViewSection({Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
     // The ViewListNotifier will be updated after ViewListData changed passed by parent widget
     return ChangeNotifierProxyProvider<ViewListNotifier, ViewSectionNotifier>(
       create: (_) {
-        final views = Provider.of<ViewListNotifier>(context, listen: false).items;
+        final views = Provider.of<ViewListNotifier>(context, listen: false).views;
         return ViewSectionNotifier(views);
       },
       update: (_, notifier, controller) => controller!..update(notifier),
@@ -63,7 +86,9 @@ class ViewSection extends StatelessWidget {
       (view) => ViewSectionItem(
         view: view,
         isSelected: _isViewSelected(context, view.id),
-        onSelected: (view) => context.read<ViewSectionNotifier>().setSelectedView(view),
+        onSelected: (view) {
+          context.read<ViewSectionNotifier>().selectView = view;
+        },
       ).padding(vertical: 4),
     );