feat: add cloumn bloc

This commit is contained in:
appflowy 2022-03-09 09:31:06 +08:00
parent 808d848f62
commit 00db755c29
15 changed files with 255 additions and 138 deletions

View File

@ -12,6 +12,7 @@ import 'package:app_flowy/workspace/application/menu/prelude.dart';
import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
@ -102,6 +103,13 @@ class HomeDepsResolver {
), ),
); );
getIt.registerFactoryParam<ColumnBloc, List<Field>, void>(
(data, _) => ColumnBloc(
data: GridColumnData(fields: data),
service: ColumnService(),
),
);
// trash // trash
getIt.registerLazySingleton<TrashService>(() => TrashService()); getIt.registerLazySingleton<TrashService>(() => TrashService());
getIt.registerLazySingleton<TrashListener>(() => TrashListener()); getIt.registerLazySingleton<TrashListener>(() => TrashListener());

View File

@ -0,0 +1,43 @@
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:dartz/dartz.dart';
import 'dart:async';
import 'column_service.dart';
import 'data.dart';
part 'column_bloc.freezed.dart';
class ColumnBloc extends Bloc<ColumnEvent, ColumnState> {
final ColumnService service;
final GridColumnData data;
ColumnBloc({required this.data, required this.service}) : super(ColumnState.initial(data.fields)) {
on<ColumnEvent>(
(event, emit) async {
await event.map(
initial: (_InitialColumn value) async {},
createColumn: (_CreateColumn value) {},
);
},
);
}
@override
Future<void> close() async {
return super.close();
}
}
@freezed
abstract class ColumnEvent with _$ColumnEvent {
const factory ColumnEvent.initial() = _InitialColumn;
const factory ColumnEvent.createColumn() = _CreateColumn;
}
@freezed
abstract class ColumnState with _$ColumnState {
const factory ColumnState({required List<Field> fields}) = _ColumnState;
factory ColumnState.initial(List<Field> fields) => ColumnState(fields: fields);
}

View File

@ -0,0 +1 @@
class ColumnService {}

View File

@ -33,3 +33,9 @@ class GridRowData {
required this.cellMap, required this.cellMap,
}); });
} }
class GridColumnData {
final List<Field> fields;
GridColumnData({required this.fields});
}

View File

@ -3,3 +3,5 @@ export 'row_bloc.dart';
export 'row_service.dart'; export 'row_service.dart';
export 'grid_service.dart'; export 'grid_service.dart';
export 'data.dart'; export 'data.dart';
export 'column_service.dart';
export 'column_bloc.dart';

View File

@ -111,7 +111,7 @@ class _GridBodyState extends State<GridBody> {
slivers: <Widget>[ slivers: <Widget>[
_buildHeader(gridInfo.fields), _buildHeader(gridInfo.fields),
_buildRows(gridInfo), _buildRows(gridInfo),
_builderFooter(context), const GridFooter(),
], ],
), ),
), ),
@ -145,12 +145,4 @@ class _GridBodyState extends State<GridBody> {
), ),
); );
} }
Widget _builderFooter(BuildContext context) {
return GridFooter(
onAddRow: () {
context.read<GridBloc>().add(const GridEvent.createRow());
},
);
}
} }

View File

@ -8,6 +8,6 @@ class GridLayout {
final fieldsWidth = fields.map((field) => field.width.toDouble()).reduce((value, element) => value + element); final fieldsWidth = fields.map((field) => field.width.toDouble()).reduce((value, element) => value + element);
return fieldsWidth + GridSize.startHeaderPadding; return fieldsWidth + GridSize.leadingHeaderPadding + GridSize.trailHeaderPadding;
} }
} }

View File

@ -1,15 +1,29 @@
class GridInsets { import 'package:flutter/widgets.dart';
static double scale = 1;
static double get horizontal => 8 * scale;
static double get vertical => 8 * scale;
}
class GridSize { class GridSize {
static double scale = 1; static double scale = 1;
static double get scrollBarSize => 12 * scale; static double get scrollBarSize => 12 * scale;
static double get headerHeight => 50 * scale; static double get headerHeight => 40 * scale;
static double get footerHeight => 40 * scale; static double get footerHeight => 40 * scale;
static double get startHeaderPadding => 30 * scale; static double get leadingHeaderPadding => 30 * scale;
static double get trailHeaderPadding => 140 * scale;
static double get headerContentPadding => 8 * scale;
static double get cellContentPadding => 8 * scale;
//
static EdgeInsets get headerContentInsets => EdgeInsets.symmetric(
horizontal: GridSize.headerContentPadding,
vertical: GridSize.headerContentPadding,
);
static EdgeInsets get cellContentInsets => EdgeInsets.symmetric(
horizontal: GridSize.cellContentPadding,
vertical: GridSize.cellContentPadding,
);
static EdgeInsets get footerContentInsets => EdgeInsets.fromLTRB(
0,
GridSize.headerContentPadding,
GridSize.headerContentPadding,
GridSize.headerContentPadding,
);
} }

View File

@ -27,7 +27,7 @@ class CellContainer extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border(right: borderSide, bottom: borderSide), border: Border(right: borderSide, bottom: borderSide),
), ),
padding: EdgeInsets.symmetric(vertical: GridInsets.vertical, horizontal: GridInsets.horizontal), padding: GridSize.cellContentInsets,
child: Center(child: IntrinsicHeight(child: child)), child: Center(child: IntrinsicHeight(child: child)),
), ),
); );

View File

@ -1,15 +1,4 @@
import 'package:app_flowy/workspace/application/grid/row_bloc.dart';
import 'package:app_flowy/workspace/presentation/home/menu/app/header/add_button.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
import 'package:flowy_infra_ui/widget/mouse_hover_builder.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'cell_decoration.dart';
// ignore: import_of_legacy_library_into_null_safe
/// The interface of base cell. /// The interface of base cell.
abstract class GridCellWidget extends StatelessWidget { abstract class GridCellWidget extends StatelessWidget {
@ -89,53 +78,3 @@ class BlankCell extends GridCellWidget {
return Container(); return Container();
} }
} }
class RowLeading extends StatelessWidget {
final String rowId;
const RowLeading({required this.rowId, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocBuilder<RowBloc, RowState>(
builder: (context, state) {
if (state.isHighlight) {
return Row(
children: const [
CreateRowButton(),
DrawRowButton(),
],
);
}
return const SizedBox.expand();
},
);
}
}
class CreateRowButton extends StatelessWidget {
const CreateRowButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return Tooltip(
message: '',
child: FlowyIconButton(
hoverColor: theme.hover,
width: 22,
onPressed: () => context.read<RowBloc>().add(const RowEvent.createRow()),
icon: svg("home/add"),
),
);
}
}
class DrawRowButton extends StatelessWidget {
const DrawRowButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container();
}
}

View File

@ -1,7 +1,9 @@
import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/prelude.dart'; import 'package:app_flowy/workspace/application/grid/prelude.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
import 'package:flowy_infra_ui/widget/mouse_hover_builder.dart'; import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.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';
import 'cell_builder.dart'; import 'cell_builder.dart';
@ -42,7 +44,10 @@ class GridRowWidget extends StatelessWidget {
List<Widget> _buildCells() { List<Widget> _buildCells() {
return [ return [
SizedBox(width: GridSize.startHeaderPadding, child: RowLeading(rowId: data.row.id)), SizedBox(
width: GridSize.leadingHeaderPadding,
child: LeadingRow(rowId: data.row.id),
),
...data.fields.map( ...data.fields.map(
(field) { (field) {
final cellData = data.cellMap[field.id]; final cellData = data.cellMap[field.id];
@ -51,7 +56,72 @@ class GridRowWidget extends StatelessWidget {
child: GridCellBuilder.buildCell(field, cellData), child: GridCellBuilder.buildCell(field, cellData),
); );
}, },
),
SizedBox(
width: GridSize.trailHeaderPadding,
child: TrailingRow(rowId: data.row.id),
) )
].toList(); ].toList();
} }
} }
class LeadingRow extends StatelessWidget {
final String rowId;
const LeadingRow({required this.rowId, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocBuilder<RowBloc, RowState>(
builder: (context, state) {
if (state.isHighlight) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
CreateRowButton(),
],
);
}
return const SizedBox.expand();
},
);
}
}
class TrailingRow extends StatelessWidget {
final String rowId;
const TrailingRow({required this.rowId, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
final borderSide = BorderSide(color: theme.shader4, width: 0.4);
return BlocBuilder<RowBloc, RowState>(
builder: (context, state) {
return Container(
width: GridSize.trailHeaderPadding,
decoration: BoxDecoration(
border: Border(bottom: borderSide),
),
padding: GridSize.cellContentInsets,
);
},
);
}
}
class CreateRowButton extends StatelessWidget {
const CreateRowButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return FlowyIconButton(
hoverColor: theme.hover,
width: 22,
onPressed: () => context.read<RowBloc>().add(const RowEvent.createRow()),
iconPadding: const EdgeInsets.all(3),
icon: svg("home/add"),
);
}
}

View File

@ -1,47 +1,45 @@
import 'package:app_flowy/workspace/application/grid/row_bloc.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
import 'package:flowy_infra_ui/widget/mouse_hover_builder.dart'; import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
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 '../content/cell_decoration.dart';
class GridFooter extends StatelessWidget { class GridFooter extends StatelessWidget {
final VoidCallback? onAddRow; const GridFooter({Key? key}) : super(key: key);
const GridFooter({Key? key, required this.onAddRow}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SliverToBoxAdapter( return SliverToBoxAdapter(
child: SizedBox( child: SizedBox(
height: GridSize.footerHeight, height: GridSize.footerHeight,
child: Padding(
padding: GridSize.headerContentInsets,
child: Row( child: Row(
children: [ children: [
AddRowButton(onTap: onAddRow), SizedBox(width: GridSize.leadingHeaderPadding),
const SizedBox(width: 120, child: AddRowButton()),
], ],
), ),
), ),
),
); );
} }
} }
class AddRowButton extends StatelessWidget { class AddRowButton extends StatelessWidget {
final VoidCallback? onTap; const AddRowButton({Key? key}) : super(key: key);
const AddRowButton({Key? key, required this.onTap}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( final theme = context.watch<AppTheme>();
behavior: HitTestBehavior.translucent, return FlowyButton(
onTap: onTap, text: const FlowyText.medium('New row', fontSize: 12),
child: MouseHoverBuilder( hoverColor: theme.hover,
builder: (_, isHovered) => Container( onTap: () => context.read<RowBloc>().add(const RowEvent.createRow()),
width: GridSize.startHeaderPadding, icon: svg("home/add"),
height: GridSize.footerHeight,
decoration: CellDecoration.box(
color: isHovered ? Colors.red.withOpacity(.1) : Colors.white,
),
child: const Icon(Icons.add, size: 16),
),
),
); );
} }
} }

View File

@ -1,6 +1,13 @@
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/grid/column_bloc.dart';
import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' hide Row;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'header_cell.dart'; import 'header_cell.dart';
@ -31,28 +38,77 @@ class GridHeaderDelegate extends SliverPersistentHeaderDelegate {
class GridHeader extends StatelessWidget { class GridHeader extends StatelessWidget {
final List<Field> fields; final List<Field> fields;
const GridHeader({required this.fields, Key? key}) : super(key: key); const GridHeader({required this.fields, Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final headers = List<Widget>.empty(growable: true); return BlocProvider(
fields.asMap().forEach((index, field) { create: (context) => getIt<ColumnBloc>(param1: fields),
final header = HeaderCellContainer( child: BlocBuilder<ColumnBloc, ColumnState>(
builder: (context, state) {
final headers = state.fields
.map(
(field) => HeaderCellContainer(
width: field.width.toDouble(), width: field.width.toDouble(),
child: HeaderCell(field), child: HeaderCell(field),
); ),
)
// .toList();
headers.add(header);
});
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
const HeaderCellLeading(), const LeadingHeaderCell(),
...headers, ...headers,
const TrailingHeaderCell(),
], ],
); );
},
),
);
}
}
class LeadingHeaderCell extends StatelessWidget {
const LeadingHeaderCell({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
width: GridSize.leadingHeaderPadding,
);
}
}
class TrailingHeaderCell extends StatelessWidget {
const TrailingHeaderCell({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
final borderSide = BorderSide(color: theme.shader4, width: 0.4);
return Container(
width: GridSize.trailHeaderPadding,
decoration: BoxDecoration(
border: Border(top: borderSide, bottom: borderSide),
),
padding: GridSize.headerContentInsets,
child: const CreateColumnButton(),
);
}
}
class CreateColumnButton extends StatelessWidget {
const CreateColumnButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return FlowyButton(
text: const FlowyText.medium('New column', fontSize: 12),
hoverColor: theme.hover,
onTap: () => context.read<ColumnBloc>().add(const ColumnEvent.createColumn()),
icon: svg("home/add"),
);
} }
} }

View File

@ -5,7 +5,6 @@ import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.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';
import 'constants.dart';
class HeaderCell extends StatelessWidget { class HeaderCell extends StatelessWidget {
final Field field; final Field field;
@ -15,7 +14,7 @@ class HeaderCell extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = context.watch<AppTheme>(); final theme = context.watch<AppTheme>();
return FlowyButton( return FlowyButton(
text: FlowyText.medium(field.name), text: FlowyText.medium(field.name, fontSize: 12),
hoverColor: theme.hover, hoverColor: theme.hover,
onTap: () {}, onTap: () {},
); );
@ -40,19 +39,8 @@ class HeaderCellContainer extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border(top: borderSide, right: borderSide, bottom: borderSide), border: Border(top: borderSide, right: borderSide, bottom: borderSide),
), ),
padding: EdgeInsets.symmetric(vertical: GridInsets.vertical, horizontal: GridInsets.horizontal), padding: GridSize.headerContentInsets,
child: child, child: child,
); );
} }
} }
class HeaderCellLeading extends StatelessWidget {
const HeaderCellLeading({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
width: GridSize.startHeaderPadding,
);
}
}

View File

@ -24,7 +24,7 @@ class FlowyButton extends StatelessWidget {
return InkWell( return InkWell(
onTap: onTap, onTap: onTap,
child: FlowyHover( child: FlowyHover(
config: HoverDisplayConfig(borderRadius: Corners.s6Border, hoverColor: hoverColor), config: HoverDisplayConfig(borderRadius: Corners.s5Border, hoverColor: hoverColor),
builder: (context, onHover) => _render(), builder: (context, onHover) => _render(),
), ),
); );