chore: opti row rebuild

This commit is contained in:
appflowy 2022-03-09 12:11:27 +08:00
parent 00db755c29
commit 321682717c
6 changed files with 105 additions and 84 deletions

View File

@ -1,4 +1,5 @@
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:equatable/equatable.dart';
class GridInfo { class GridInfo {
List<Row> rows; List<Row> rows;
@ -23,15 +24,18 @@ class GridInfo {
} }
} }
class GridRowData { class GridRowData extends Equatable {
Row row; final Row row;
List<Field> fields; final List<Field> fields;
Map<String, Cell> cellMap; final Map<String, Cell> cellMap;
GridRowData({ const GridRowData({
required this.row, required this.row,
required this.fields, required this.fields,
required this.cellMap, required this.cellMap,
}); });
@override
List<Object> get props => [row.hashCode, cellMap];
} }
class GridColumnData { class GridColumnData {

View File

@ -1,6 +1,5 @@
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 'package:dartz/dartz.dart';
import 'dart:async'; import 'dart:async';
import 'data.dart'; import 'data.dart';
import 'row_service.dart'; import 'row_service.dart';
@ -9,18 +8,18 @@ part 'row_bloc.freezed.dart';
class RowBloc extends Bloc<RowEvent, RowState> { class RowBloc extends Bloc<RowEvent, RowState> {
final RowService service; final RowService service;
final GridRowData data;
RowBloc({required this.data, required this.service}) : super(RowState.initial()) { RowBloc({required GridRowData data, required this.service}) : super(RowState.initial(data)) {
on<RowEvent>( on<RowEvent>(
(event, emit) async { (event, emit) async {
await event.map( await event.map(
initial: (_InitialRow value) async {}, initial: (_InitialRow value) async {},
createRow: (_CreateRow value) {}, createRow: (_CreateRow value) {},
highlightRow: (_HighlightRow value) { activeRow: (_ActiveRow value) {
emit(state.copyWith( emit(state.copyWith(active: true));
isHighlight: value.rowId.fold(() => false, (rowId) => rowId == data.row.id), },
)); disactiveRow: (_DisactiveRow value) {
emit(state.copyWith(active: false));
}, },
); );
}, },
@ -35,16 +34,18 @@ class RowBloc extends Bloc<RowEvent, RowState> {
@freezed @freezed
abstract class RowEvent with _$RowEvent { abstract class RowEvent with _$RowEvent {
const factory RowEvent.initial() = _InitialRow; const factory RowEvent.initial(GridRowData data) = _InitialRow;
const factory RowEvent.createRow() = _CreateRow; const factory RowEvent.createRow() = _CreateRow;
const factory RowEvent.highlightRow(Option<String> rowId) = _HighlightRow; const factory RowEvent.activeRow() = _ActiveRow;
const factory RowEvent.disactiveRow() = _DisactiveRow;
} }
@freezed @freezed
abstract class RowState with _$RowState { abstract class RowState with _$RowState {
const factory RowState({ const factory RowState({
required bool isHighlight, required GridRowData data,
required bool active,
}) = _RowState; }) = _RowState;
factory RowState.initial() => const RowState(isHighlight: false); factory RowState.initial(GridRowData data) => RowState(data: data, active: false);
} }

View File

@ -139,7 +139,7 @@ class _GridBodyState extends State<GridBody> {
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(context, index) { (context, index) {
final data = gridInfo.rowAtIndex(index); final data = gridInfo.rowAtIndex(index);
return RepaintBoundary(child: GridRowWidget(data)); return RepaintBoundary(child: GridRowWidget(data: data));
}, },
childCount: gridInfo.numberOfRows(), childCount: gridInfo.numberOfRows(),
), ),

View File

@ -1,9 +1,10 @@
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/theme.dart'; import 'package:flowy_infra/theme.dart';
import 'package:flowy_sdk/log.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 CellContainer extends StatelessWidget { class CellContainer extends StatefulWidget {
final Widget child; final Widget child;
final double width; final double width;
const CellContainer({ const CellContainer({
@ -12,23 +13,27 @@ class CellContainer extends StatelessWidget {
required this.width, required this.width,
}) : super(key: key); }) : super(key: key);
@override
State<CellContainer> createState() => _CellContainerState();
}
class _CellContainerState extends State<CellContainer> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = context.watch<AppTheme>(); final theme = context.watch<AppTheme>();
final borderSide = BorderSide(color: theme.shader4, width: 0.4); final borderSide = BorderSide(color: theme.shader4, width: 0.4);
return GestureDetector( return GestureDetector(
behavior: HitTestBehavior.translucent, behavior: HitTestBehavior.translucent,
onTap: () {}, onTap: () {},
child: Container( child: Container(
constraints: BoxConstraints( constraints: BoxConstraints(
maxWidth: width, maxWidth: widget.width,
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border(right: borderSide, bottom: borderSide), border: Border(right: borderSide, bottom: borderSide),
), ),
padding: GridSize.cellContentInsets, padding: GridSize.cellContentInsets,
child: Center(child: IntrinsicHeight(child: child)), child: Center(child: IntrinsicHeight(child: widget.child)),
), ),
); );
} }

View File

@ -8,88 +8,103 @@ 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';
import 'cell_container.dart'; import 'cell_container.dart';
import 'grid_cell.dart';
import 'package:dartz/dartz.dart';
class GridRowWidget extends StatelessWidget { class GridRowWidget extends StatefulWidget {
final GridRowData data; final GridRowData data;
final Function(bool)? onHoverChange; GridRowWidget({required this.data, Key? key}) : super(key: ObjectKey(data.row.id));
const GridRowWidget(this.data, {Key? key, this.onHoverChange}) : super(key: key);
@override
State<GridRowWidget> createState() => _GridRowWidgetState();
}
class _GridRowWidgetState extends State<GridRowWidget> {
late RowBloc _rowBloc;
@override
void initState() {
_rowBloc = getIt<RowBloc>(param1: widget.data);
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider.value(
create: (context) => getIt<RowBloc>(param1: data), value: _rowBloc,
child: BlocBuilder<RowBloc, RowState>( child: GestureDetector(
builder: (context, state) { behavior: HitTestBehavior.translucent,
return GestureDetector( child: MouseRegion(
behavior: HitTestBehavior.translucent, cursor: SystemMouseCursors.click,
child: MouseRegion( onEnter: (p) => _rowBloc.add(const RowEvent.activeRow()),
cursor: SystemMouseCursors.click, onExit: (p) => _rowBloc.add(const RowEvent.disactiveRow()),
onEnter: (p) => context.read<RowBloc>().add(RowEvent.highlightRow(some(data.row.id))), child: SizedBox(
onExit: (p) => context.read<RowBloc>().add(RowEvent.highlightRow(none())), height: _rowBloc.state.data.row.height.toDouble(),
child: SizedBox( child: Row(
height: data.row.height.toDouble(), crossAxisAlignment: CrossAxisAlignment.stretch,
child: Row( children: [
crossAxisAlignment: CrossAxisAlignment.stretch, const LeadingRow(),
children: _buildCells(), _buildCells(),
), const TrailingRow(),
), ],
), ),
); ),
}, ),
), ),
); );
} }
List<Widget> _buildCells() { @override
return [ Future<void> dispose() async {
SizedBox( _rowBloc.close();
width: GridSize.leadingHeaderPadding, super.dispose();
child: LeadingRow(rowId: data.row.id), }
),
...data.fields.map( Widget _buildCells() {
(field) { return BlocBuilder<RowBloc, RowState>(
final cellData = data.cellMap[field.id]; buildWhen: (p, c) => p.data != c.data,
return CellContainer( builder: (context, state) {
width: field.width.toDouble(), return Row(
child: GridCellBuilder.buildCell(field, cellData), key: ValueKey(state.data.row.id),
); children: state.data.fields.map(
}, (field) {
), final cellData = state.data.cellMap[field.id];
SizedBox( return CellContainer(
width: GridSize.trailHeaderPadding, width: field.width.toDouble(),
child: TrailingRow(rowId: data.row.id), child: GridCellBuilder.buildCell(field, cellData),
) );
].toList(); },
).toList(),
);
},
);
} }
} }
class LeadingRow extends StatelessWidget { class LeadingRow extends StatelessWidget {
final String rowId; const LeadingRow({Key? key}) : super(key: key);
const LeadingRow({required this.rowId, Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<RowBloc, RowState>( return BlocSelector<RowBloc, RowState, bool>(
builder: (context, state) { selector: (state) => state.active,
if (state.isHighlight) { builder: (context, isActive) {
return Row( return SizedBox(
mainAxisAlignment: MainAxisAlignment.center, width: GridSize.leadingHeaderPadding,
children: const [ child: isActive
CreateRowButton(), ? Row(
], mainAxisAlignment: MainAxisAlignment.center,
); children: const [
} CreateRowButton(),
return const SizedBox.expand(); ],
)
: null,
);
}, },
); );
} }
} }
class TrailingRow extends StatelessWidget { class TrailingRow extends StatelessWidget {
final String rowId; const TrailingRow({Key? key}) : super(key: key);
const TrailingRow({required this.rowId, Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -18,10 +18,6 @@ class HeaderCell extends StatelessWidget {
hoverColor: theme.hover, hoverColor: theme.hover,
onTap: () {}, onTap: () {},
); );
// return Text(
// field.name,
// style: const TextStyle(fontSize: 15.0, color: Colors.black),
// );
} }
} }