Skip to content

Commit e125e94

Browse files
authored
fix: offset row actions by cover height (#6390)
1 parent 5fbd07e commit e125e94

File tree

2 files changed

+89
-46
lines changed

2 files changed

+89
-46
lines changed

frontend/appflowy_flutter/lib/plugins/database/widgets/row/row_banner.dart

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ import 'package:universal_platform/universal_platform.dart';
3939

4040
import '../../../document/presentation/editor_plugins/plugins.dart';
4141

42-
const _coverHeight = 250.0;
42+
/// We have the cover height as public as it is used in the row_detail.dart file
43+
/// Used to determine the position of the row actions depending on if there is a cover or not.
44+
///
45+
const rowCoverHeight = 250.0;
46+
4347
const _iconHeight = 60.0;
4448
const _toolbarHeight = 40.0;
4549

@@ -181,11 +185,11 @@ class _RowBannerState extends State<RowBanner> {
181185
double _calculateOverallHeight(bool hasIcon, bool hasCover) {
182186
switch ((hasIcon, hasCover)) {
183187
case (true, true):
184-
return _coverHeight + _toolbarHeight;
188+
return rowCoverHeight + _toolbarHeight;
185189
case (true, false):
186190
return 50 + _iconHeight + _toolbarHeight;
187191
case (false, true):
188-
return _coverHeight + _toolbarHeight;
192+
return rowCoverHeight + _toolbarHeight;
189193
case (false, false):
190194
return _toolbarHeight;
191195
}
@@ -224,7 +228,7 @@ class _RowCoverState extends State<RowCover> {
224228
@override
225229
Widget build(BuildContext context) {
226230
return SizedBox(
227-
height: _coverHeight,
231+
height: rowCoverHeight,
228232
child: MouseRegion(
229233
onEnter: (_) => setState(() => isOverlayButtonsHidden = false),
230234
onExit: (_) => setState(() => isOverlayButtonsHidden = true),
@@ -364,7 +368,7 @@ class _DesktopRowCoverState extends State<DesktopRowCover> {
364368
Widget build(BuildContext context) {
365369
if (cover.coverType == CoverTypePB.FileCover) {
366370
return SizedBox(
367-
height: _coverHeight,
371+
height: rowCoverHeight,
368372
width: double.infinity,
369373
child: AFImage(
370374
url: cover.data,
@@ -376,7 +380,7 @@ class _DesktopRowCoverState extends State<DesktopRowCover> {
376380

377381
if (cover.coverType == CoverTypePB.AssetCover) {
378382
return SizedBox(
379-
height: _coverHeight,
383+
height: rowCoverHeight,
380384
width: double.infinity,
381385
child: Image.asset(
382386
PageStyleCoverImageType.builtInImagePath(cover.data),
@@ -389,15 +393,15 @@ class _DesktopRowCoverState extends State<DesktopRowCover> {
389393
final color = FlowyTint.fromId(cover.data)?.color(context) ??
390394
cover.data.tryToColor();
391395
return Container(
392-
height: _coverHeight,
396+
height: rowCoverHeight,
393397
width: double.infinity,
394398
color: color,
395399
);
396400
}
397401

398402
if (cover.coverType == CoverTypePB.GradientCover) {
399403
return Container(
400-
height: _coverHeight,
404+
height: rowCoverHeight,
401405
width: double.infinity,
402406
decoration: BoxDecoration(
403407
gradient: FlowyGradientColor.fromId(cover.data).linear,

frontend/appflowy_flutter/lib/plugins/database/widgets/row/row_detail.dart

Lines changed: 77 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:flutter/foundation.dart';
12
import 'package:flutter/material.dart';
23

34
import 'package:appflowy/generated/flowy_svgs.g.dart';
@@ -41,13 +42,26 @@ class RowDetailPage extends StatefulWidget with FlowyOverlayDelegate {
4142
}
4243

4344
class _RowDetailPageState extends State<RowDetailPage> {
44-
final scrollController = ScrollController();
4545
late final cellBuilder = EditableCellBuilder(
4646
databaseController: widget.databaseController,
4747
);
48+
late final ScrollController scrollController;
49+
50+
double scrollOffset = 0;
51+
52+
@override
53+
void initState() {
54+
super.initState();
55+
scrollController = ScrollController(
56+
onAttach: (_) => attachScrollListener(),
57+
);
58+
}
59+
60+
void attachScrollListener() => scrollController.addListener(onScrollChanged);
4861

4962
@override
5063
void dispose() {
64+
scrollController.removeListener(onScrollChanged);
5165
scrollController.dispose();
5266
super.dispose();
5367
}
@@ -65,53 +79,78 @@ class _RowDetailPageState extends State<RowDetailPage> {
6579
),
6680
BlocProvider.value(value: getIt<ReminderBloc>()),
6781
],
68-
child: Stack(
69-
children: [
70-
ListView(
71-
controller: scrollController,
72-
children: [
73-
RowBanner(
74-
databaseController: widget.databaseController,
75-
rowController: widget.rowController,
76-
cellBuilder: cellBuilder,
77-
allowOpenAsFullPage: widget.allowOpenAsFullPage,
78-
userProfile: widget.userProfile,
79-
),
80-
const VSpace(16),
81-
Padding(
82-
padding: const EdgeInsets.only(left: 40, right: 60),
83-
child: RowPropertyList(
82+
child: BlocBuilder<RowDetailBloc, RowDetailState>(
83+
builder: (context, state) => Stack(
84+
children: [
85+
ListView(
86+
controller: scrollController,
87+
physics: const ClampingScrollPhysics(),
88+
children: [
89+
RowBanner(
90+
databaseController: widget.databaseController,
91+
rowController: widget.rowController,
8492
cellBuilder: cellBuilder,
85-
viewId: widget.databaseController.viewId,
86-
fieldController: widget.databaseController.fieldController,
93+
allowOpenAsFullPage: widget.allowOpenAsFullPage,
94+
userProfile: widget.userProfile,
8795
),
96+
const VSpace(16),
97+
Padding(
98+
padding: const EdgeInsets.only(left: 40, right: 60),
99+
child: RowPropertyList(
100+
cellBuilder: cellBuilder,
101+
viewId: widget.databaseController.viewId,
102+
fieldController:
103+
widget.databaseController.fieldController,
104+
),
105+
),
106+
const VSpace(20),
107+
const Padding(
108+
padding: EdgeInsets.symmetric(horizontal: 60),
109+
child: Divider(height: 1.0),
110+
),
111+
const VSpace(20),
112+
RowDocument(
113+
viewId: widget.rowController.viewId,
114+
rowId: widget.rowController.rowId,
115+
),
116+
],
117+
),
118+
Positioned(
119+
top: calculateActionsOffset(
120+
state.rowMeta.cover.data.isNotEmpty,
88121
),
89-
const VSpace(20),
90-
const Padding(
91-
padding: EdgeInsets.symmetric(horizontal: 60),
92-
child: Divider(height: 1.0),
93-
),
94-
const VSpace(20),
95-
RowDocument(
96-
viewId: widget.rowController.viewId,
97-
rowId: widget.rowController.rowId,
122+
right: 12,
123+
child: Row(
124+
children: actions(context),
98125
),
99-
],
100-
),
101-
Positioned(
102-
top: 12,
103-
right: 12,
104-
child: Row(
105-
children: _actions(context),
106126
),
107-
),
108-
],
127+
],
128+
),
109129
),
110130
),
111131
);
112132
}
113133

114-
List<Widget> _actions(BuildContext context) {
134+
void onScrollChanged() {
135+
if (scrollOffset != scrollController.offset) {
136+
setState(() => scrollOffset = scrollController.offset);
137+
}
138+
}
139+
140+
double calculateActionsOffset(bool hasCover) {
141+
if (!hasCover) {
142+
return 12;
143+
}
144+
145+
final offsetByScroll = clampDouble(
146+
rowCoverHeight - scrollOffset,
147+
0,
148+
rowCoverHeight,
149+
);
150+
return 12 + offsetByScroll;
151+
}
152+
153+
List<Widget> actions(BuildContext context) {
115154
return [
116155
if (widget.allowOpenAsFullPage) ...[
117156
FlowyTooltip(

0 commit comments

Comments
 (0)