Merge pull request #423 from gaganyadav80/drag_drop_docs

feat: adds drag and drop support to pages and docs
This commit is contained in:
Nathan.fooo 2022-04-26 09:20:14 +08:00 committed by GitHub
commit c7d34465e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 170 additions and 37 deletions

View File

@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:developer';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/view/view_ext.dart';
@ -7,6 +8,7 @@ import 'package:app_flowy/workspace/presentation/home/menu/menu.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:reorderables/reorderables.dart';
import 'package:styled_widget/styled_widget.dart';
import 'item.dart';
@ -27,29 +29,111 @@ class ViewSection extends StatelessWidget {
},
update: (_, notifier, controller) => controller!..update(notifier),
child: Consumer(builder: (context, ViewSectionNotifier notifier, child) {
return _renderSectionItems(context, notifier.views);
return RenderSectionItems(views: notifier.views);
}),
);
}
Widget _renderSectionItems(BuildContext context, List<View> views) {
List<Widget> viewWidgets = [];
if (views.isNotEmpty) {
viewWidgets = views
.map(
(view) => ViewSectionItem(
view: view,
isSelected: _isViewSelected(context, view.id),
onSelected: (view) {
context.read<ViewSectionNotifier>().selectedView = view;
Provider.of<MenuSharedState>(context, listen: false).selectedView.value = view;
},
).padding(vertical: 4),
)
.toList(growable: false);
// Widget _renderSectionItems(BuildContext context, List<View> views) {
// List<Widget> viewWidgets = [];
// if (views.isNotEmpty) {
// viewWidgets = views
// .map(
// (view) => ViewSectionItem(
// view: view,
// isSelected: _isViewSelected(context, view.id),
// onSelected: (view) {
// context.read<ViewSectionNotifier>().selectedView = view;
// Provider.of<MenuSharedState>(context, listen: false).selectedView.value = view;
// },
// ).padding(vertical: 4),
// )
// .toList(growable: false);
// }
// return Column(children: viewWidgets);
// }
// bool _isViewSelected(BuildContext context, String viewId) {
// final view = context.read<ViewSectionNotifier>().selectedView;
// if (view == null) {
// return false;
// }
// return view.id == viewId;
// }
}
class RenderSectionItems extends StatefulWidget {
const RenderSectionItems({Key? key, required this.views}) : super(key: key);
final List<View> views;
@override
State<RenderSectionItems> createState() => _RenderSectionItemsState();
}
class _RenderSectionItemsState extends State<RenderSectionItems> {
List<View> views = <View>[];
/// Maps the hasmap value of the section items to their index in the reorderable list.
//TODO @gaganyadav80: Retain this map to persist the order of the items.
final Map<String, int> _sectionItemIndex = <String, int>{};
void _initItemList() {
views.addAll(widget.views);
for (int i = 0; i < views.length; i++) {
if (_sectionItemIndex[views[i].id] == null) {
_sectionItemIndex[views[i].id] = i;
}
}
}
@override
void initState() {
super.initState();
_initItemList();
}
@override
Widget build(BuildContext context) {
if (views.isEmpty) {
_initItemList();
}
return Column(children: viewWidgets);
log("BUILD: Section items: ${views.length}");
return ReorderableColumn(
needsLongPressDraggable: false,
onReorder: (oldIndex, index) {
setState(() {
// int index = newIndex > oldIndex ? newIndex - 1 : newIndex;
View section = views.removeAt(oldIndex);
views.insert(index, section);
_sectionItemIndex[section.id] = index;
});
},
children: List.generate(
views.length,
(index) {
return Container(
key: ValueKey(views[index].id),
child: views
.map(
(view) => ViewSectionItem(
view: view,
isSelected: _isViewSelected(context, view.id),
onSelected: (view) {
context.read<ViewSectionNotifier>().selectedView = view;
Provider.of<MenuSharedState>(context, listen: false).selectedView.value = view;
},
).padding(vertical: 4),
)
.toList()[index],
);
},
),
);
}
bool _isViewSelected(BuildContext context, String viewId) {

View File

@ -27,7 +27,7 @@ import 'app/menu_app.dart';
import 'app/create_button.dart';
import 'menu_user.dart';
class HomeMenu extends StatelessWidget {
class HomeMenu extends StatefulWidget {
final PublishNotifier<bool> _collapsedNotifier;
final UserProfile user;
final CurrentWorkspaceSetting workspaceSetting;
@ -40,13 +40,22 @@ class HomeMenu extends StatelessWidget {
}) : _collapsedNotifier = collapsedNotifier,
super(key: key);
@override
State<HomeMenu> createState() => _HomeMenuState();
}
class _HomeMenuState extends State<HomeMenu> {
/// Maps the hashmap of the menu items to their index in reorderable list view.
//TODO @gaganyadav80: Retain this map to persist on app restarts.
final Map<int, int> _menuItemIndex = <int, int>{};
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<MenuBloc>(
create: (context) {
final menuBloc = getIt<MenuBloc>(param1: user, param2: workspaceSetting.workspace.id);
final menuBloc = getIt<MenuBloc>(param1: widget.user, param2: widget.workspaceSetting.workspace.id);
menuBloc.add(const MenuEvent.initial());
return menuBloc;
},
@ -63,7 +72,7 @@ class HomeMenu extends StatelessWidget {
BlocListener<MenuBloc, MenuState>(
listenWhen: (p, c) => p.isCollapse != c.isCollapse,
listener: (context, state) {
_collapsedNotifier.value = state.isCollapse;
widget._collapsedNotifier.value = state.isCollapse;
},
)
],
@ -80,7 +89,8 @@ class HomeMenu extends StatelessWidget {
return Container(
color: theme.bg1,
child: ChangeNotifierProvider(
create: (_) => MenuSharedState(view: workspaceSetting.hasLatestView() ? workspaceSetting.latestView : null),
create: (_) =>
MenuSharedState(view: widget.workspaceSetting.hasLatestView() ? widget.workspaceSetting.latestView : null),
child: Consumer(builder: (context, MenuSharedState sharedState, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.start,
@ -115,26 +125,65 @@ class HomeMenu extends StatelessWidget {
child: BlocSelector<MenuBloc, MenuState, List<Widget>>(
selector: (state) {
List<Widget> menuItems = [];
menuItems.add(MenuUser(user));
// menuItems.add(MenuUser(user));
List<MenuApp> appWidgets =
state.apps.foldRight([], (apps, _) => apps.map((app) => MenuApp(app)).toList());
menuItems.addAll(appWidgets);
// menuItems.addAll(appWidgets);
for (int i = 0; i < appWidgets.length; i++) {
if (_menuItemIndex[appWidgets[i].key.hashCode] == null) {
_menuItemIndex[appWidgets[i].key.hashCode] = i;
}
menuItems.insert(_menuItemIndex[appWidgets[i].key.hashCode]!, appWidgets[i]);
}
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];
},
),
builder: (context, menuItems) {
return ReorderableListView.builder(
itemCount: menuItems.length,
buildDefaultDragHandles: false,
header: Padding(
padding: EdgeInsets.only(bottom: 20.0 - MenuAppSizes.appVPadding),
child: MenuUser(widget.user),
),
onReorder: (oldIndex, newIndex) {
int index = newIndex > oldIndex ? newIndex - 1 : newIndex;
Widget menu = menuItems.removeAt(oldIndex);
menuItems.insert(index, menu);
final menuBloc = context.read<MenuBloc>();
menuBloc.state.apps.forEach((a) {
var app = a.removeAt(oldIndex);
a.insert(index, app);
});
_menuItemIndex[menu.key.hashCode] = index;
},
physics: StyledScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
//? @gaganyadav80: To mimic the ListView.separated behavior, we need to add a padding.
// EdgeInsets padding = EdgeInsets.zero;
// if (index == 0) {
// padding = EdgeInsets.only(bottom: MenuAppSizes.appVPadding / 2);
// } else if (index == menuItems.length - 1) {
// padding = EdgeInsets.only(top: MenuAppSizes.appVPadding / 2);
// } else {
// padding = EdgeInsets.symmetric(vertical: MenuAppSizes.appVPadding / 2);
// }
return ReorderableDragStartListener(
key: ValueKey(menuItems[index].hashCode),
index: index,
child: Padding(
padding: EdgeInsets.symmetric(vertical: MenuAppSizes.appVPadding / 2),
child: menuItems[index],
),
);
},
);
},
),
),
),