chore: fixed grid toolbar

This commit is contained in:
appflowy 2022-04-13 10:47:07 +08:00
parent a707410548
commit b0e07c952e
6 changed files with 135 additions and 119 deletions

View File

@ -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/row/row_service.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:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@ -35,6 +35,10 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
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) {
result.fold(
(field) => _loadCellData(),
(field) => add(DateCellEvent.didReceiveFieldUpdate(field)),
(err) => Log.error(err),
);
});
@ -97,6 +101,7 @@ class DateCellEvent with _$DateCellEvent {
const factory DateCellEvent.initial() = _InitialCell;
const factory DateCellEvent.selectDay(DateTime day) = _SelectDay;
const factory DateCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
const factory DateCellEvent.didReceiveFieldUpdate(Field field) = _DidReceiveFieldUpdate;
}
@freezed
@ -104,11 +109,13 @@ class DateCellState with _$DateCellState {
const factory DateCellState({
required CellData cellData,
required String content,
required Field field,
DateTime? selectedDay,
}) = _DateCellState;
factory DateCellState.initial(CellData cellData) => DateCellState(
cellData: cellData,
field: cellData.field,
content: cellData.cell?.content ?? "",
);
}

View File

@ -20,6 +20,15 @@ class FieldService {
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({
required String fieldId,
String? name,

View File

@ -40,7 +40,7 @@ class _GridPageState extends State<GridPage> {
return state.loadingState.map(
loading: (_) => const Center(child: CircularProgressIndicator.adaptive()),
finish: (result) => result.successOrFail.fold(
(_) => const FlowyGrid(),
(_) => FlowyGrid(),
(err) => FlowyErrorPage(err.toString()),
),
);
@ -65,64 +65,61 @@ class _GridPageState extends State<GridPage> {
}
}
class FlowyGrid extends StatefulWidget {
const FlowyGrid({Key? key}) : super(key: key);
@override
_FlowyGridState createState() => _FlowyGridState();
}
class _FlowyGridState extends State<FlowyGrid> {
class FlowyGrid extends StatelessWidget {
final _scrollController = GridScrollController();
final _key = GlobalKey<SliverAnimatedListState>();
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
FlowyGrid({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocBuilder<GridBloc, GridState>(
buildWhen: (previous, current) => previous.fields.length != current.fields.length,
builder: (context, state) {
if (state.fields.isEmpty) {
return const Center(child: CircularProgressIndicator.adaptive());
}
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(),
const SliverToBoxAdapter(child: GridFooter()),
],
),
),
final child = _wrapScrollView(
state.fields,
[
_GridHeader(gridId: state.gridId, fields: List.from(state.fields)),
_GridRows(),
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(
axis: Axis.vertical,
controller: _scrollController.verticalController,
barSize: GridSize.scrollBarSize,
child: StyledSingleChildScrollView(
controller: _scrollController.horizontalController,
axis: Axis.horizontal,
child: child,
),
child: horizontalScrollView,
);
}
}
@ -140,14 +137,27 @@ class _GridToolbarAdaptor extends StatelessWidget {
);
},
builder: (context, toolbarContext) {
return SliverToBoxAdapter(
child: GridToolbar(toolbarContext: toolbarContext),
);
return 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 {
final _key = GlobalKey<SliverAnimatedListState>();
_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()),
],
),
),
),
),
);
}
}

View File

@ -74,7 +74,13 @@ final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day);
class _CellCalendar extends StatefulWidget with FlowyOverlayDelegate {
final void Function(DateTime) onSelected;
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
State<_CellCalendar> createState() => _CellCalendarState();
@ -86,7 +92,11 @@ class _CellCalendar extends StatefulWidget with FlowyOverlayDelegate {
}) async {
_CellCalendar.remove(context);
final calendar = _CellCalendar(onSelected: onSelected, onDismissed: onDismissed);
final calendar = _CellCalendar(
onSelected: onSelected,
onDismissed: onDismissed,
includeTime: false,
);
// const size = Size(460, 400);
// final window = await getWindowInfo();
// FlowyOverlay.of(context).insertWithRect(
@ -105,11 +115,11 @@ class _CellCalendar extends StatefulWidget with FlowyOverlayDelegate {
FlowyOverlay.of(context).insertWithAnchor(
widget: OverlayContainer(
child: calendar,
constraints: BoxConstraints.loose(const Size(300, 320)),
constraints: BoxConstraints.tight(const Size(320, 320)),
),
identifier: _CellCalendar.identifier(),
anchorContext: context,
anchorDirection: AnchorDirection.bottomWithCenterAligned,
anchorDirection: AnchorDirection.leftWithCenterAligned,
style: FlowyOverlayStyle(blur: false),
delegate: calendar,
);

View File

@ -7,28 +7,8 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class GridFooter extends StatelessWidget {
const GridFooter({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);
class GridAddRowButton extends StatelessWidget {
const GridAddRowButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {

View File

@ -12,30 +12,32 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'field_editor.dart';
import 'field_cell.dart';
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: _GridHeaderDelegate(gridId: gridId, fields: List.from(fields)),
floating: true,
pinned: true,
);
}
}
class _GridHeaderDelegate extends SliverPersistentHeaderDelegate {
class GridHeaderSliverAdaptor extends SliverPersistentHeaderDelegate {
final String gridId;
final List<Field> fields;
_GridHeaderDelegate({required this.gridId, required this.fields});
GridHeaderSliverAdaptor({required this.gridId, required this.fields});
@override
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
@ -46,40 +48,13 @@ class _GridHeaderDelegate extends SliverPersistentHeaderDelegate {
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
if (oldDelegate is _GridHeaderDelegate) {
if (oldDelegate is GridHeaderSliverAdaptor) {
return fields.length != oldDelegate.fields.length;
}
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 {
const _CellLeading({Key? key}) : super(key: key);