Parcourir la source

chore: opti row rebuild

appflowy il y a 3 ans
Parent
commit
321682717c

+ 9 - 5
frontend/app_flowy/lib/workspace/application/grid/data.dart

@@ -1,4 +1,5 @@
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:equatable/equatable.dart';
 
 class GridInfo {
   List<Row> rows;
@@ -23,15 +24,18 @@ class GridInfo {
   }
 }
 
-class GridRowData {
-  Row row;
-  List<Field> fields;
-  Map<String, Cell> cellMap;
-  GridRowData({
+class GridRowData extends Equatable {
+  final Row row;
+  final List<Field> fields;
+  final Map<String, Cell> cellMap;
+  const GridRowData({
     required this.row,
     required this.fields,
     required this.cellMap,
   });
+
+  @override
+  List<Object> get props => [row.hashCode, cellMap];
 }
 
 class GridColumnData {

+ 12 - 11
frontend/app_flowy/lib/workspace/application/grid/row_bloc.dart

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

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/grid_page.dart

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

+ 9 - 4
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/cell_container.dart

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

+ 74 - 59
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/content/grid_row.dart

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

+ 0 - 4
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/header_cell.dart

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