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: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-grid-data-model/grid.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.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
getIt.registerLazySingleton<TrashService>(() => TrashService());
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,
});
}
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 'grid_service.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>[
_buildHeader(gridInfo.fields),
_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);
return fieldsWidth + GridSize.startHeaderPadding;
return fieldsWidth + GridSize.leadingHeaderPadding + GridSize.trailHeaderPadding;
}
}

View File

@ -1,15 +1,29 @@
class GridInsets {
static double scale = 1;
static double get horizontal => 8 * scale;
static double get vertical => 8 * scale;
}
import 'package:flutter/widgets.dart';
class GridSize {
static double scale = 1;
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 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(
border: Border(right: borderSide, bottom: borderSide),
),
padding: EdgeInsets.symmetric(vertical: GridInsets.vertical, horizontal: GridInsets.horizontal),
padding: GridSize.cellContentInsets,
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_bloc/flutter_bloc.dart';
import 'cell_decoration.dart';
// ignore: import_of_legacy_library_into_null_safe
/// The interface of base cell.
abstract class GridCellWidget extends StatelessWidget {
@ -89,53 +78,3 @@ class BlankCell extends GridCellWidget {
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/workspace/application/grid/prelude.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_bloc/flutter_bloc.dart';
import 'cell_builder.dart';
@ -42,7 +44,10 @@ class GridRowWidget extends StatelessWidget {
List<Widget> _buildCells() {
return [
SizedBox(width: GridSize.startHeaderPadding, child: RowLeading(rowId: data.row.id)),
SizedBox(
width: GridSize.leadingHeaderPadding,
child: LeadingRow(rowId: data.row.id),
),
...data.fields.map(
(field) {
final cellData = data.cellMap[field.id];
@ -51,7 +56,72 @@ class GridRowWidget extends StatelessWidget {
child: GridCellBuilder.buildCell(field, cellData),
);
},
),
SizedBox(
width: GridSize.trailHeaderPadding,
child: TrailingRow(rowId: data.row.id),
)
].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,22 +1,28 @@
import 'package:app_flowy/workspace/application/grid/row_bloc.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 '../content/cell_decoration.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class GridFooter extends StatelessWidget {
final VoidCallback? onAddRow;
const GridFooter({Key? key, required this.onAddRow}) : super(key: key);
const GridFooter({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: SizedBox(
height: GridSize.footerHeight,
child: Row(
children: [
AddRowButton(onTap: onAddRow),
],
child: Padding(
padding: GridSize.headerContentInsets,
child: Row(
children: [
SizedBox(width: GridSize.leadingHeaderPadding),
const SizedBox(width: 120, child: AddRowButton()),
],
),
),
),
);
@ -24,24 +30,16 @@ class GridFooter extends StatelessWidget {
}
class AddRowButton extends StatelessWidget {
final VoidCallback? onTap;
const AddRowButton({Key? key, required this.onTap}) : super(key: key);
const AddRowButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: onTap,
child: MouseHoverBuilder(
builder: (_, isHovered) => Container(
width: GridSize.startHeaderPadding,
height: GridSize.footerHeight,
decoration: CellDecoration.box(
color: isHovered ? Colors.red.withOpacity(.1) : Colors.white,
),
child: const Icon(Icons.add, size: 16),
),
),
final theme = context.watch<AppTheme>();
return FlowyButton(
text: const FlowyText.medium('New row', fontSize: 12),
hoverColor: theme.hover,
onTap: () => context.read<RowBloc>().add(const RowEvent.createRow()),
icon: svg("home/add"),
);
}
}

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: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:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'header_cell.dart';
@ -31,28 +38,77 @@ class GridHeaderDelegate extends SliverPersistentHeaderDelegate {
class GridHeader extends StatelessWidget {
final List<Field> fields;
const GridHeader({required this.fields, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final headers = List<Widget>.empty(growable: true);
fields.asMap().forEach((index, field) {
final header = HeaderCellContainer(
width: field.width.toDouble(),
child: HeaderCell(field),
);
return BlocProvider(
create: (context) => getIt<ColumnBloc>(param1: fields),
child: BlocBuilder<ColumnBloc, ColumnState>(
builder: (context, state) {
final headers = state.fields
.map(
(field) => HeaderCellContainer(
width: field.width.toDouble(),
child: HeaderCell(field),
),
)
.toList();
//
headers.add(header);
});
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const HeaderCellLeading(),
...headers,
],
return Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const LeadingHeaderCell(),
...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:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'constants.dart';
class HeaderCell extends StatelessWidget {
final Field field;
@ -15,7 +14,7 @@ class HeaderCell extends StatelessWidget {
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
return FlowyButton(
text: FlowyText.medium(field.name),
text: FlowyText.medium(field.name, fontSize: 12),
hoverColor: theme.hover,
onTap: () {},
);
@ -40,19 +39,8 @@ class HeaderCellContainer extends StatelessWidget {
decoration: BoxDecoration(
border: Border(top: borderSide, right: borderSide, bottom: borderSide),
),
padding: EdgeInsets.symmetric(vertical: GridInsets.vertical, horizontal: GridInsets.horizontal),
padding: GridSize.headerContentInsets,
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(
onTap: onTap,
child: FlowyHover(
config: HoverDisplayConfig(borderRadius: Corners.s6Border, hoverColor: hoverColor),
config: HoverDisplayConfig(borderRadius: Corners.s5Border, hoverColor: hoverColor),
builder: (context, onHover) => _render(),
),
);