mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-04-25 07:07:32 -04:00
chore: fixed grid toolbar
This commit is contained in:
parent
a707410548
commit
b0e07c952e
6 changed files with 135 additions and 119 deletions
|
@ -2,7 +2,7 @@ import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_listener.dar
|
||||||
import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
|
import 'package:app_flowy/workspace/application/grid/field/field_listener.dart';
|
||||||
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
|
||||||
import 'package:flowy_sdk/log.dart';
|
import 'package:flowy_sdk/log.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
|
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell, Field;
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
@ -35,6 +35,10 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
|
||||||
content: value.cell.content,
|
content: value.cell.content,
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
|
didReceiveFieldUpdate: (_DidReceiveFieldUpdate value) {
|
||||||
|
emit(state.copyWith(field: value.field));
|
||||||
|
_loadCellData();
|
||||||
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -58,7 +62,7 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
|
||||||
|
|
||||||
_fieldListener.updateFieldNotifier.addPublishListener((result) {
|
_fieldListener.updateFieldNotifier.addPublishListener((result) {
|
||||||
result.fold(
|
result.fold(
|
||||||
(field) => _loadCellData(),
|
(field) => add(DateCellEvent.didReceiveFieldUpdate(field)),
|
||||||
(err) => Log.error(err),
|
(err) => Log.error(err),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -97,6 +101,7 @@ class DateCellEvent with _$DateCellEvent {
|
||||||
const factory DateCellEvent.initial() = _InitialCell;
|
const factory DateCellEvent.initial() = _InitialCell;
|
||||||
const factory DateCellEvent.selectDay(DateTime day) = _SelectDay;
|
const factory DateCellEvent.selectDay(DateTime day) = _SelectDay;
|
||||||
const factory DateCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
|
const factory DateCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
|
||||||
|
const factory DateCellEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
|
@ -104,11 +109,13 @@ class DateCellState with _$DateCellState {
|
||||||
const factory DateCellState({
|
const factory DateCellState({
|
||||||
required CellData cellData,
|
required CellData cellData,
|
||||||
required String content,
|
required String content,
|
||||||
|
required Field field,
|
||||||
DateTime? selectedDay,
|
DateTime? selectedDay,
|
||||||
}) = _DateCellState;
|
}) = _DateCellState;
|
||||||
|
|
||||||
factory DateCellState.initial(CellData cellData) => DateCellState(
|
factory DateCellState.initial(CellData cellData) => DateCellState(
|
||||||
cellData: cellData,
|
cellData: cellData,
|
||||||
|
field: cellData.field,
|
||||||
content: cellData.cell?.content ?? "",
|
content: cellData.cell?.content ?? "",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,15 @@ class FieldService {
|
||||||
return GridEventSwitchToField(payload).send();
|
return GridEventSwitchToField(payload).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Either<EditFieldContext, FlowyError>> getEditFieldContext(String fieldId, FieldType fieldType) {
|
||||||
|
final payload = GetEditFieldContextPayload.create()
|
||||||
|
..gridId = gridId
|
||||||
|
..fieldId = fieldId
|
||||||
|
..fieldType = fieldType;
|
||||||
|
|
||||||
|
return GridEventGetEditFieldContext(payload).send();
|
||||||
|
}
|
||||||
|
|
||||||
Future<Either<Unit, FlowyError>> updateField({
|
Future<Either<Unit, FlowyError>> updateField({
|
||||||
required String fieldId,
|
required String fieldId,
|
||||||
String? name,
|
String? name,
|
||||||
|
|
|
@ -40,7 +40,7 @@ class _GridPageState extends State<GridPage> {
|
||||||
return state.loadingState.map(
|
return state.loadingState.map(
|
||||||
loading: (_) => const Center(child: CircularProgressIndicator.adaptive()),
|
loading: (_) => const Center(child: CircularProgressIndicator.adaptive()),
|
||||||
finish: (result) => result.successOrFail.fold(
|
finish: (result) => result.successOrFail.fold(
|
||||||
(_) => const FlowyGrid(),
|
(_) => FlowyGrid(),
|
||||||
(err) => FlowyErrorPage(err.toString()),
|
(err) => FlowyErrorPage(err.toString()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -65,64 +65,61 @@ class _GridPageState extends State<GridPage> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FlowyGrid extends StatefulWidget {
|
class FlowyGrid extends StatelessWidget {
|
||||||
const FlowyGrid({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_FlowyGridState createState() => _FlowyGridState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FlowyGridState extends State<FlowyGrid> {
|
|
||||||
final _scrollController = GridScrollController();
|
final _scrollController = GridScrollController();
|
||||||
final _key = GlobalKey<SliverAnimatedListState>();
|
FlowyGrid({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_scrollController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<GridBloc, GridState>(
|
return BlocBuilder<GridBloc, GridState>(
|
||||||
buildWhen: (previous, current) => previous.fields.length != current.fields.length,
|
buildWhen: (previous, current) => previous.fields.length != current.fields.length,
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
if (state.fields.isEmpty) {
|
final child = _wrapScrollView(
|
||||||
return const Center(child: CircularProgressIndicator.adaptive());
|
state.fields,
|
||||||
}
|
[
|
||||||
|
_GridHeader(gridId: state.gridId, fields: List.from(state.fields)),
|
||||||
final child = SizedBox(
|
|
||||||
width: GridLayout.headerWidth(state.fields),
|
|
||||||
child: ScrollConfiguration(
|
|
||||||
behavior: const ScrollBehavior().copyWith(scrollbars: false),
|
|
||||||
child: CustomScrollView(
|
|
||||||
physics: StyledScrollPhysics(),
|
|
||||||
controller: _scrollController.verticalController,
|
|
||||||
slivers: [
|
|
||||||
const _GridToolbarAdaptor(),
|
|
||||||
GridHeader(gridId: state.gridId, fields: List.from(state.fields)),
|
|
||||||
_GridRows(),
|
_GridRows(),
|
||||||
const SliverToBoxAdapter(child: GridFooter()),
|
const _GridFooter(),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return _wrapScrollbar(child);
|
return Column(children: [
|
||||||
|
const _GridToolbarAdaptor(),
|
||||||
|
Flexible(child: child),
|
||||||
|
]);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _wrapScrollbar(Widget child) {
|
Widget _wrapScrollView(
|
||||||
|
List<Field> fields,
|
||||||
|
List<Widget> slivers,
|
||||||
|
) {
|
||||||
|
final verticalScrollView = ScrollConfiguration(
|
||||||
|
behavior: const ScrollBehavior().copyWith(scrollbars: false),
|
||||||
|
child: CustomScrollView(
|
||||||
|
physics: StyledScrollPhysics(),
|
||||||
|
controller: _scrollController.verticalController,
|
||||||
|
slivers: slivers,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final sizedVerticalScrollView = SizedBox(
|
||||||
|
width: GridLayout.headerWidth(fields),
|
||||||
|
child: verticalScrollView,
|
||||||
|
);
|
||||||
|
|
||||||
|
final horizontalScrollView = StyledSingleChildScrollView(
|
||||||
|
controller: _scrollController.horizontalController,
|
||||||
|
axis: Axis.horizontal,
|
||||||
|
child: sizedVerticalScrollView,
|
||||||
|
);
|
||||||
|
|
||||||
return ScrollbarListStack(
|
return ScrollbarListStack(
|
||||||
axis: Axis.vertical,
|
axis: Axis.vertical,
|
||||||
controller: _scrollController.verticalController,
|
controller: _scrollController.verticalController,
|
||||||
barSize: GridSize.scrollBarSize,
|
barSize: GridSize.scrollBarSize,
|
||||||
child: StyledSingleChildScrollView(
|
child: horizontalScrollView,
|
||||||
controller: _scrollController.horizontalController,
|
|
||||||
axis: Axis.horizontal,
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,14 +137,27 @@ class _GridToolbarAdaptor extends StatelessWidget {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
builder: (context, toolbarContext) {
|
builder: (context, toolbarContext) {
|
||||||
return SliverToBoxAdapter(
|
return GridToolbar(toolbarContext: toolbarContext);
|
||||||
child: GridToolbar(toolbarContext: toolbarContext),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _GridHeader extends StatelessWidget {
|
||||||
|
final String gridId;
|
||||||
|
final List<Field> fields;
|
||||||
|
const _GridHeader({Key? key, required this.gridId, required this.fields}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SliverPersistentHeader(
|
||||||
|
delegate: GridHeaderSliverAdaptor(gridId: gridId, fields: List.from(fields)),
|
||||||
|
floating: true,
|
||||||
|
pinned: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _GridRows extends StatelessWidget {
|
class _GridRows extends StatelessWidget {
|
||||||
final _key = GlobalKey<SliverAnimatedListState>();
|
final _key = GlobalKey<SliverAnimatedListState>();
|
||||||
_GridRows({Key? key}) : super(key: key);
|
_GridRows({Key? key}) : super(key: key);
|
||||||
|
@ -191,3 +201,28 @@ class _GridRows extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _GridFooter extends StatelessWidget {
|
||||||
|
const _GridFooter({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SliverPadding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 200),
|
||||||
|
sliver: SliverToBoxAdapter(
|
||||||
|
child: SizedBox(
|
||||||
|
height: GridSize.footerHeight,
|
||||||
|
child: Padding(
|
||||||
|
padding: GridSize.headerContentInsets,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(width: GridSize.leadingHeaderPadding),
|
||||||
|
const SizedBox(width: 120, child: GridAddRowButton()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -74,7 +74,13 @@ final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day);
|
||||||
class _CellCalendar extends StatefulWidget with FlowyOverlayDelegate {
|
class _CellCalendar extends StatefulWidget with FlowyOverlayDelegate {
|
||||||
final void Function(DateTime) onSelected;
|
final void Function(DateTime) onSelected;
|
||||||
final VoidCallback onDismissed;
|
final VoidCallback onDismissed;
|
||||||
const _CellCalendar({required this.onSelected, required this.onDismissed, Key? key}) : super(key: key);
|
final bool includeTime;
|
||||||
|
const _CellCalendar({
|
||||||
|
required this.onSelected,
|
||||||
|
required this.onDismissed,
|
||||||
|
required this.includeTime,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_CellCalendar> createState() => _CellCalendarState();
|
State<_CellCalendar> createState() => _CellCalendarState();
|
||||||
|
@ -86,7 +92,11 @@ class _CellCalendar extends StatefulWidget with FlowyOverlayDelegate {
|
||||||
}) async {
|
}) async {
|
||||||
_CellCalendar.remove(context);
|
_CellCalendar.remove(context);
|
||||||
|
|
||||||
final calendar = _CellCalendar(onSelected: onSelected, onDismissed: onDismissed);
|
final calendar = _CellCalendar(
|
||||||
|
onSelected: onSelected,
|
||||||
|
onDismissed: onDismissed,
|
||||||
|
includeTime: false,
|
||||||
|
);
|
||||||
// const size = Size(460, 400);
|
// const size = Size(460, 400);
|
||||||
// final window = await getWindowInfo();
|
// final window = await getWindowInfo();
|
||||||
// FlowyOverlay.of(context).insertWithRect(
|
// FlowyOverlay.of(context).insertWithRect(
|
||||||
|
@ -105,11 +115,11 @@ class _CellCalendar extends StatefulWidget with FlowyOverlayDelegate {
|
||||||
FlowyOverlay.of(context).insertWithAnchor(
|
FlowyOverlay.of(context).insertWithAnchor(
|
||||||
widget: OverlayContainer(
|
widget: OverlayContainer(
|
||||||
child: calendar,
|
child: calendar,
|
||||||
constraints: BoxConstraints.loose(const Size(300, 320)),
|
constraints: BoxConstraints.tight(const Size(320, 320)),
|
||||||
),
|
),
|
||||||
identifier: _CellCalendar.identifier(),
|
identifier: _CellCalendar.identifier(),
|
||||||
anchorContext: context,
|
anchorContext: context,
|
||||||
anchorDirection: AnchorDirection.bottomWithCenterAligned,
|
anchorDirection: AnchorDirection.leftWithCenterAligned,
|
||||||
style: FlowyOverlayStyle(blur: false),
|
style: FlowyOverlayStyle(blur: false),
|
||||||
delegate: calendar,
|
delegate: calendar,
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,28 +7,8 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class GridFooter extends StatelessWidget {
|
class GridAddRowButton extends StatelessWidget {
|
||||||
const GridFooter({Key? key}) : super(key: key);
|
const GridAddRowButton({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return SizedBox(
|
|
||||||
height: GridSize.footerHeight,
|
|
||||||
child: Padding(
|
|
||||||
padding: GridSize.headerContentInsets,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
SizedBox(width: GridSize.leadingHeaderPadding),
|
|
||||||
const SizedBox(width: 120, child: _AddRowButton()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _AddRowButton extends StatelessWidget {
|
|
||||||
const _AddRowButton({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
|
@ -12,30 +12,32 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'field_editor.dart';
|
import 'field_editor.dart';
|
||||||
import 'field_cell.dart';
|
import 'field_cell.dart';
|
||||||
|
|
||||||
class GridHeader extends StatelessWidget {
|
class GridHeaderSliverAdaptor extends SliverPersistentHeaderDelegate {
|
||||||
final String gridId;
|
|
||||||
final List<Field> fields;
|
|
||||||
const GridHeader({Key? key, required this.gridId, required this.fields}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return SliverPersistentHeader(
|
|
||||||
delegate: _GridHeaderDelegate(gridId: gridId, fields: List.from(fields)),
|
|
||||||
floating: true,
|
|
||||||
pinned: true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _GridHeaderDelegate extends SliverPersistentHeaderDelegate {
|
|
||||||
final String gridId;
|
final String gridId;
|
||||||
final List<Field> fields;
|
final List<Field> fields;
|
||||||
|
|
||||||
_GridHeaderDelegate({required this.gridId, required this.fields});
|
GridHeaderSliverAdaptor({required this.gridId, required this.fields});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
|
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||||
return _GridHeaderWidget(gridId: gridId, fields: fields, key: ObjectKey(fields));
|
final cells = fields.map(
|
||||||
|
(field) => GridFieldCell(
|
||||||
|
GridFieldCellContext(gridId: gridId, field: field),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
color: Colors.white,
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
const _CellLeading(),
|
||||||
|
...cells,
|
||||||
|
_CellTrailing(gridId: gridId),
|
||||||
|
],
|
||||||
|
key: ObjectKey(fields),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -46,40 +48,13 @@ class _GridHeaderDelegate extends SliverPersistentHeaderDelegate {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
|
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
|
||||||
if (oldDelegate is _GridHeaderDelegate) {
|
if (oldDelegate is GridHeaderSliverAdaptor) {
|
||||||
return fields.length != oldDelegate.fields.length;
|
return fields.length != oldDelegate.fields.length;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _GridHeaderWidget extends StatelessWidget {
|
|
||||||
final String gridId;
|
|
||||||
final List<Field> fields;
|
|
||||||
|
|
||||||
const _GridHeaderWidget({required this.gridId, required this.fields, Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final cells = fields.map(
|
|
||||||
(field) => GridFieldCell(
|
|
||||||
GridFieldCellContext(gridId: gridId, field: field),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
||||||
children: [
|
|
||||||
const _CellLeading(),
|
|
||||||
...cells,
|
|
||||||
_CellTrailing(gridId: gridId),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
// return Container(height: GridSize.headerHeight, color: theme.surface, child: row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CellLeading extends StatelessWidget {
|
class _CellLeading extends StatelessWidget {
|
||||||
const _CellLeading({Key? key}) : super(key: key);
|
const _CellLeading({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue