mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: scroll to bottom when create new card
This commit is contained in:
parent
aba0f946dd
commit
d6162159aa
@ -198,7 +198,10 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
|
||||
List<AFColumnItem> _buildRows(GroupPB group) {
|
||||
final items = group.rows.map((row) {
|
||||
return BoardColumnItem(row: row, fieldId: group.fieldId);
|
||||
return BoardColumnItem(
|
||||
row: row,
|
||||
fieldId: group.fieldId,
|
||||
);
|
||||
}).toList();
|
||||
|
||||
return <AFColumnItem>[...items];
|
||||
@ -286,17 +289,18 @@ class BoardColumnItem extends AFColumnItem {
|
||||
|
||||
final String fieldId;
|
||||
|
||||
BoardColumnItem({required this.row, required this.fieldId});
|
||||
final bool requestFocus;
|
||||
|
||||
BoardColumnItem({
|
||||
required this.row,
|
||||
required this.fieldId,
|
||||
this.requestFocus = false,
|
||||
});
|
||||
|
||||
@override
|
||||
String get id => row.id;
|
||||
}
|
||||
|
||||
class CreateCardItem extends AFColumnItem {
|
||||
@override
|
||||
String get id => '$CreateCardItem';
|
||||
}
|
||||
|
||||
class GroupControllerDelegateImpl extends GroupControllerDelegate {
|
||||
final AFBoardDataController controller;
|
||||
|
||||
@ -304,10 +308,18 @@ class GroupControllerDelegateImpl extends GroupControllerDelegate {
|
||||
|
||||
@override
|
||||
void insertRow(GroupPB group, RowPB row, int? index) {
|
||||
final item = BoardColumnItem(row: row, fieldId: group.fieldId);
|
||||
if (index != null) {
|
||||
final item = BoardColumnItem(
|
||||
row: row,
|
||||
fieldId: group.fieldId,
|
||||
);
|
||||
controller.insertColumnItem(group.groupId, index, item);
|
||||
} else {
|
||||
final item = BoardColumnItem(
|
||||
row: row,
|
||||
fieldId: group.fieldId,
|
||||
requestFocus: true,
|
||||
);
|
||||
controller.addColumnItem(group.groupId, item);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'board_column/board_column.dart';
|
||||
@ -141,18 +143,22 @@ class AFBoardContent extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _AFBoardContentState extends State<AFBoardContent> {
|
||||
late _BoardColumnState columnState;
|
||||
|
||||
final GlobalKey _columnContainerOverlayKey =
|
||||
GlobalKey(debugLabel: '$AFBoardContent overlay key');
|
||||
late BoardOverlayEntry _overlayEntry;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
columnState = _BoardColumnState();
|
||||
_overlayEntry = BoardOverlayEntry(
|
||||
builder: (BuildContext context) {
|
||||
final interceptor = OverlappingDragTargetInterceptor(
|
||||
reorderFlexId: widget.dataController.identifier,
|
||||
acceptedReorderFlexId: widget.dataController.columnIds,
|
||||
delegate: widget.delegate,
|
||||
columnKeys: UnmodifiableMapView(columnState.columnKeys),
|
||||
);
|
||||
|
||||
final reorderFlex = ReorderFlex(
|
||||
@ -165,7 +171,7 @@ class _AFBoardContentState extends State<AFBoardContent> {
|
||||
dataSource: widget.dataController,
|
||||
direction: Axis.horizontal,
|
||||
interceptor: interceptor,
|
||||
children: _buildColumns(interceptor.columnKeys),
|
||||
children: _buildColumns(),
|
||||
);
|
||||
|
||||
return Stack(
|
||||
@ -197,7 +203,7 @@ class _AFBoardContentState extends State<AFBoardContent> {
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildColumns(List<ColumnKey> columnKeys) {
|
||||
List<Widget> _buildColumns() {
|
||||
final List<Widget> children =
|
||||
widget.dataController.columnDatas.asMap().entries.map(
|
||||
(item) {
|
||||
@ -222,21 +228,14 @@ class _AFBoardContentState extends State<AFBoardContent> {
|
||||
footBuilder: widget.footBuilder,
|
||||
cardBuilder: widget.cardBuilder,
|
||||
dataSource: dataSource,
|
||||
scrollController: ScrollController(),
|
||||
scrollController: columnState.scrollController(columnData.id),
|
||||
phantomController: widget.phantomController,
|
||||
onReorder: widget.dataController.moveColumnItem,
|
||||
cornerRadius: widget.config.cornerRadius,
|
||||
backgroundColor: widget.config.columnBackgroundColor,
|
||||
);
|
||||
|
||||
// columnKeys
|
||||
// .removeWhere((element) => element.columnId == columnData.id);
|
||||
// columnKeys.add(
|
||||
// ColumnKey(
|
||||
// columnId: columnData.id,
|
||||
// key: boardColumn.columnGlobalKey,
|
||||
// ),
|
||||
// );
|
||||
columnState.cacheColumn(columnData.id, boardColumn.globalKey);
|
||||
|
||||
return ConstrainedBox(
|
||||
constraints: widget.columnConstraints,
|
||||
@ -297,3 +296,26 @@ class _BoardColumnDataSourceImpl extends AFBoardColumnDataDataSource {
|
||||
@override
|
||||
List<String> get acceptedColumnIds => dataController.columnIds;
|
||||
}
|
||||
|
||||
class _BoardColumnState {
|
||||
final Map<String, GlobalKey> columnKeys = {};
|
||||
|
||||
void cacheColumn(String columnId, GlobalKey key) {
|
||||
columnKeys[columnId] = key;
|
||||
}
|
||||
|
||||
ScrollController scrollController(String columnId) {
|
||||
final flexGlobalKey = columnKeys[columnId];
|
||||
var scrollController = ScrollController();
|
||||
if (flexGlobalKey != null) {
|
||||
// assert(flexGlobalKey.currentWidget is ReorderFlex);
|
||||
|
||||
// if (flexGlobalKey.currentWidget is ReorderFlex) {
|
||||
// final reorderFlex = flexGlobalKey.currentWidget as ReorderFlex;
|
||||
// final offset = reorderFlex.scrollController!.offset;
|
||||
// scrollController = ScrollController(initialScrollOffset: offset);
|
||||
// }
|
||||
}
|
||||
return scrollController;
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,9 @@ class AFBoardColumnWidget extends StatefulWidget {
|
||||
|
||||
final Color backgroundColor;
|
||||
|
||||
const AFBoardColumnWidget({
|
||||
final GlobalKey globalKey = GlobalKey();
|
||||
|
||||
AFBoardColumnWidget({
|
||||
Key? key,
|
||||
this.headerBuilder,
|
||||
this.footBuilder,
|
||||
@ -114,10 +116,13 @@ class _AFBoardColumnWidgetState extends State<AFBoardColumnWidget> {
|
||||
final GlobalKey _columnOverlayKey =
|
||||
GlobalKey(debugLabel: '$AFBoardColumnWidget overlay key');
|
||||
|
||||
late GlobalObjectKey _indexGlobalKey;
|
||||
|
||||
late BoardOverlayEntry _overlayEntry;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_indexGlobalKey = GlobalObjectKey(widget.key!);
|
||||
_overlayEntry = BoardOverlayEntry(
|
||||
builder: (BuildContext context) {
|
||||
final children = widget.dataSource.columnData.items
|
||||
@ -138,7 +143,6 @@ class _AFBoardColumnWidgetState extends State<AFBoardColumnWidget> {
|
||||
);
|
||||
|
||||
Widget reorderFlex = ReorderFlex(
|
||||
key: widget.key,
|
||||
scrollController: widget.scrollController,
|
||||
config: widget.config,
|
||||
onDragStarted: (index) {
|
||||
@ -161,6 +165,8 @@ class _AFBoardColumnWidgetState extends State<AFBoardColumnWidget> {
|
||||
children: children,
|
||||
);
|
||||
|
||||
reorderFlex = KeyedSubtree(key: _indexGlobalKey, child: reorderFlex);
|
||||
|
||||
return Container(
|
||||
margin: widget.margin,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
@ -172,10 +178,7 @@ class _AFBoardColumnWidgetState extends State<AFBoardColumnWidget> {
|
||||
children: [
|
||||
if (header != null) header,
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: widget.itemMargin,
|
||||
child: reorderFlex,
|
||||
),
|
||||
child: Padding(padding: widget.itemMargin, child: reorderFlex),
|
||||
),
|
||||
if (footer != null) footer,
|
||||
],
|
||||
|
@ -39,7 +39,7 @@ class ReorderDragTarget<T extends DragTargetData> extends StatefulWidget {
|
||||
final Widget child;
|
||||
final T dragTargetData;
|
||||
|
||||
final GlobalObjectKey _indexGlobalKey;
|
||||
final GlobalObjectKey indexGlobalKey;
|
||||
|
||||
/// Called when dragTarget is being dragging.
|
||||
final DragTargetOnStarted onDragStarted;
|
||||
@ -69,9 +69,10 @@ class ReorderDragTarget<T extends DragTargetData> extends StatefulWidget {
|
||||
|
||||
final bool useMoveAnimation;
|
||||
|
||||
ReorderDragTarget({
|
||||
const ReorderDragTarget({
|
||||
Key? key,
|
||||
required this.child,
|
||||
required this.indexGlobalKey,
|
||||
required this.dragTargetData,
|
||||
required this.onDragStarted,
|
||||
required this.onDragEnded,
|
||||
@ -82,8 +83,7 @@ class ReorderDragTarget<T extends DragTargetData> extends StatefulWidget {
|
||||
this.onAccept,
|
||||
this.onLeave,
|
||||
this.draggableTargetBuilder,
|
||||
}) : _indexGlobalKey = GlobalObjectKey(child.key!),
|
||||
super(key: key);
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ReorderDragTarget<T>> createState() => _ReorderDragTargetState<T>();
|
||||
@ -112,7 +112,7 @@ class _ReorderDragTargetState<T extends DragTargetData>
|
||||
},
|
||||
);
|
||||
|
||||
dragTarget = KeyedSubtree(key: widget._indexGlobalKey, child: dragTarget);
|
||||
dragTarget = KeyedSubtree(key: widget.indexGlobalKey, child: dragTarget);
|
||||
return dragTarget;
|
||||
}
|
||||
|
||||
@ -150,7 +150,7 @@ class _ReorderDragTargetState<T extends DragTargetData>
|
||||
child: widget.child,
|
||||
),
|
||||
onDragStarted: () {
|
||||
_draggingFeedbackSize = widget._indexGlobalKey.currentContext?.size;
|
||||
_draggingFeedbackSize = widget.indexGlobalKey.currentContext?.size;
|
||||
widget.onDragStarted(
|
||||
widget.child,
|
||||
widget.dragTargetData.draggingIndex,
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@ -55,13 +56,14 @@ class OverlappingDragTargetInterceptor extends DragTargetInterceptor {
|
||||
final String reorderFlexId;
|
||||
final List<String> acceptedReorderFlexId;
|
||||
final OverlapDragTargetDelegate delegate;
|
||||
final List<ColumnKey> columnKeys = [];
|
||||
final UnmodifiableMapView<String, GlobalKey> columnKeys;
|
||||
Timer? _delayOperation;
|
||||
|
||||
OverlappingDragTargetInterceptor({
|
||||
required this.delegate,
|
||||
required this.reorderFlexId,
|
||||
required this.acceptedReorderFlexId,
|
||||
required this.columnKeys,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -105,12 +107,6 @@ class OverlappingDragTargetInterceptor extends DragTargetInterceptor {
|
||||
}
|
||||
}
|
||||
|
||||
class ColumnKey {
|
||||
String columnId;
|
||||
GlobalKey key;
|
||||
ColumnKey({required this.columnId, required this.key});
|
||||
}
|
||||
|
||||
abstract class CrossReorderFlexDragTargetDelegate {
|
||||
/// * [reorderFlexId] is the id that the [ReorderFlex] passed in.
|
||||
bool acceptNewDragTargetData(
|
||||
|
@ -74,7 +74,7 @@ class ReorderFlex extends StatefulWidget {
|
||||
|
||||
final DragTargetInterceptor? interceptor;
|
||||
|
||||
const ReorderFlex({
|
||||
ReorderFlex({
|
||||
Key? key,
|
||||
this.scrollController,
|
||||
required this.dataSource,
|
||||
@ -85,7 +85,9 @@ class ReorderFlex extends StatefulWidget {
|
||||
this.onDragEnded,
|
||||
this.interceptor,
|
||||
this.direction = Axis.vertical,
|
||||
}) : super(key: key);
|
||||
}) : assert(children.every((Widget w) => w.key != null),
|
||||
'All child must have a key.'),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
State<ReorderFlex> createState() => ReorderFlexState();
|
||||
@ -112,10 +114,13 @@ class ReorderFlexState extends State<ReorderFlex>
|
||||
|
||||
late ReorderFlexNotifier _notifier;
|
||||
|
||||
late Map<String, GlobalObjectKey> _childKeys;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_notifier = ReorderFlexNotifier();
|
||||
dragState = DraggingState(widget.reorderFlexId);
|
||||
_childKeys = {};
|
||||
|
||||
_animation = DragTargetAnimation(
|
||||
reorderAnimationDuration: widget.config.reorderAnimationDuration,
|
||||
@ -159,7 +164,11 @@ class ReorderFlexState extends State<ReorderFlex>
|
||||
|
||||
for (int i = 0; i < widget.children.length; i += 1) {
|
||||
Widget child = widget.children[i];
|
||||
children.add(_wrap(child, i));
|
||||
final item = widget.dataSource.items[i];
|
||||
|
||||
final indexGlobalKey = GlobalObjectKey(child.key!);
|
||||
_childKeys[item.id] = indexGlobalKey;
|
||||
children.add(_wrap(child, i, indexGlobalKey));
|
||||
|
||||
// if (widget.config.useMovePlaceholder) {
|
||||
// children.add(DragTargeMovePlaceholder(
|
||||
@ -168,6 +177,9 @@ class ReorderFlexState extends State<ReorderFlex>
|
||||
// ));
|
||||
// }
|
||||
}
|
||||
Future.delayed(Duration(seconds: 3), () {
|
||||
scrollToBottom();
|
||||
});
|
||||
|
||||
final child = _wrapContainer(children);
|
||||
return _wrapScrollView(child: child);
|
||||
@ -203,10 +215,10 @@ class ReorderFlexState extends State<ReorderFlex>
|
||||
|
||||
/// [child]: the child will be wrapped with dartTarget
|
||||
/// [childIndex]: the index of the child in a list
|
||||
Widget _wrap(Widget child, int childIndex) {
|
||||
Widget _wrap(Widget child, int childIndex, GlobalObjectKey indexGlobalKey) {
|
||||
return Builder(builder: (context) {
|
||||
final ReorderDragTarget dragTarget =
|
||||
_buildDragTarget(context, child, childIndex);
|
||||
_buildDragTarget(context, child, childIndex, indexGlobalKey);
|
||||
int shiftedIndex = childIndex;
|
||||
|
||||
if (dragState.isOverlapWithPhantom()) {
|
||||
@ -312,10 +324,15 @@ class ReorderFlexState extends State<ReorderFlex>
|
||||
}
|
||||
|
||||
ReorderDragTarget _buildDragTarget(
|
||||
BuildContext builderContext, Widget child, int dragTargetIndex) {
|
||||
BuildContext builderContext,
|
||||
Widget child,
|
||||
int dragTargetIndex,
|
||||
GlobalObjectKey indexGlobalKey,
|
||||
) {
|
||||
final ReoderFlexItem reorderFlexItem =
|
||||
widget.dataSource.items[dragTargetIndex];
|
||||
return ReorderDragTarget<FlexDragTargetData>(
|
||||
indexGlobalKey: indexGlobalKey,
|
||||
dragTargetData: FlexDragTargetData(
|
||||
draggingIndex: dragTargetIndex,
|
||||
reorderFlexId: widget.reorderFlexId,
|
||||
@ -515,6 +532,34 @@ class ReorderFlexState extends State<ReorderFlex>
|
||||
}
|
||||
}
|
||||
|
||||
void scrollToBottom() {
|
||||
if (_scrolling) return;
|
||||
|
||||
if (widget.dataSource.items.isNotEmpty) {
|
||||
final item = widget.dataSource.items.last;
|
||||
final indexKey = _childKeys[item.id];
|
||||
if (indexKey == null) return;
|
||||
|
||||
final indexContext = indexKey.currentContext;
|
||||
if (indexContext == null) return;
|
||||
if (_scrollController.hasClients == false) return;
|
||||
|
||||
final renderObject = indexContext.findRenderObject();
|
||||
if (renderObject != null) {
|
||||
_scrolling = true;
|
||||
_scrollController.position
|
||||
.ensureVisible(
|
||||
renderObject,
|
||||
alignment: 0.5,
|
||||
duration: const Duration(milliseconds: 120),
|
||||
)
|
||||
.then((value) {
|
||||
setState(() => _scrolling = false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scrolls to a target context if that context is not on the screen.
|
||||
void _scrollTo(BuildContext context) {
|
||||
if (_scrolling) return;
|
||||
|
Loading…
Reference in New Issue
Block a user