flutter icon indicating copy to clipboard operation
flutter copied to clipboard

[two_dimensional_scrollables] change the width of columns without rebuilding the cells.

Open leavky opened this issue 1 year ago • 11 comments

Use case

Can you provide a controller that allows me to change the width of columns without rebuilding the cells, but triggers a manual refresh of the cell layout? I want to implement this feature recording

Proposal

I want to implement this feature recording

leavky avatar Mar 13 '24 02:03 leavky

This doesn't sound like a feature request, but it could be an implementation or package issue. Could you please share a minimal sample code for this?

huycozy avatar Mar 13 '24 09:03 huycozy

// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:two_dimensional_scrollables/two_dimensional_scrollables.dart';

void main() {
  runApp(const TableExampleApp());
}

/// A sample application that utilizes the TableView API.
class TableExampleApp extends StatelessWidget {
  /// Creates an instance of the TableView example app.
  const TableExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Table Example',
      theme: ThemeData(
        useMaterial3: true,
      ),
      home: const TableExample(),
    );
  }
}

/// The class containing the TableView for the sample application.
class TableExample extends StatefulWidget {
  /// Creates a screen that demonstrates the TableView widget.
  const TableExample({super.key});

  @override
  State<TableExample> createState() => _TableExampleState();
}

class _TableExampleState extends State<TableExample> {
  late final ScrollController _verticalController = ScrollController();
  int _rowCount = 20;

  double threeColumnWidth = 50.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Table Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 50),
        child: TableView.builder(
          verticalDetails: ScrollableDetails.vertical(controller: _verticalController),
          cellBuilder: _buildCell,
          columnCount: 20,
          columnBuilder: _buildColumnSpan,
          rowCount: _rowCount,
          rowBuilder: _buildRowSpan,
        ),
      ),
      persistentFooterButtons: <Widget>[
        Slider(
          value: threeColumnWidth,
          max: 500,
          onChanged: (newValue) {
            // change three column width
            // need a controller to change column width and don't rebuild cells
            setState(() {
              threeColumnWidth = newValue;
            });
          },
        ),
      ],
    );
  }

  TableViewCell _buildCell(BuildContext context, TableVicinity vicinity) {
    return TableViewCell(
      child: RepaintBoundary(
        child: TableCell(vicinity: vicinity),
      ),
    );
  }

  TableSpan _buildColumnSpan(int index) {
    const TableSpanDecoration decoration = TableSpanDecoration(
      border: TableSpanBorder(
        trailing: BorderSide(),
      ),
    );

    if (index == 3) {
      return TableSpan(
        foregroundDecoration: decoration,
        extent: FixedTableSpanExtent(threeColumnWidth),
        onEnter: (_) => print('Entered column $index'),
      );
    }

    return TableSpan(
      foregroundDecoration: decoration,
      extent: const FixedTableSpanExtent(100),
      onEnter: (_) => print('Entered column $index'),
    );
  }

  TableSpan _buildRowSpan(int index) {
    final TableSpanDecoration decoration = TableSpanDecoration(
      color: index.isEven ? Colors.purple[100] : null,
      border: const TableSpanBorder(
        trailing: BorderSide(
          width: 3,
        ),
      ),
    );

    return TableSpan(
      backgroundDecoration: decoration,
      extent: const FixedTableSpanExtent(65),
      cursor: SystemMouseCursors.click,
    );
  }
}

class TableCell extends StatefulWidget {
  const TableCell({super.key, required this.vicinity});

  final TableVicinity vicinity;

  @override
  State<TableCell> createState() => _TableCellState();
}

class _TableCellState extends State<TableCell> {
  @override
  Widget build(BuildContext context) {
    print("build ${widget.vicinity}");
    return Center(
      child: Text('Tile c: ${widget.vicinity.column}, r: ${widget.vicinity.row}'),
    );
  }

  @override
  void didUpdateWidget(covariant TableCell oldWidget) {
    // TODO: implement didUpdateWidget
    super.didUpdateWidget(oldWidget);
    return;
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
  }
}

recording

leavky avatar Mar 13 '24 11:03 leavky

I want change column width and don't rebuild cells

leavky avatar Mar 13 '24 11:03 leavky

I think this is working as intended since you refreshed whole TableView with setState call on Slider value changes; then cell widget will be refreshed as well. I'm not sure if there is a way to prevent cells from rebuilding.

More specifically, could you share what the use case this request needs? (to optimize performance or to implement something else?)

huycozy avatar Mar 14 '24 05:03 huycozy

I need to to optimize performance, because when cell is other's widget like checkbox, image,when change column with real time, it gets really stuck.

when the table like this.

image

I think when change the column width(or row height), don't rebuild cells.

I dont know how to change column width(or row height) and not need refreshed TableView, please let me know if there is such a method. If there have a controller relayout cells, will improve performance. Also, can rewrite the layoutChildSequence function, animation effects can be achieved.

leavky avatar Mar 14 '24 15:03 leavky

Thanks for the update. Marking this as a feature request with the use case above.

huycozy avatar Mar 15 '24 05:03 huycozy

Hi @leavky, I am not sure I understand the request to resize without rebuilding, wouldn't the cells need to be rebuilt to reflect the new dimensions?

Piinks avatar Mar 21 '24 17:03 Piinks

@Piinks ,when change the target's column width, not need rebuild other column's cell. when change column with real time, it gets really stuck. when change the target's column width, other column's cell just relayout, not need to rebuild.

leavky avatar Mar 23 '24 06:03 leavky

just like this, recording

leavky avatar Mar 23 '24 06:03 leavky

I have another question, i want eidt cell, but the cell's space is small, so i need a bigger cell to edit cell content. like this image

But i found the child's drawing order is fixed, is there have a method that let target cell paint later. thanks!

I want 1-1 on the top of 1-2, image

leavky avatar Mar 23 '24 09:03 leavky

just like this, recording recording

It seems that needs to be rebuilt too in the example you show because the text is getting shorter and needs to show an overflow effect.

As @Piinks says here:

Hi @leavky, I am not sure I understand the request to resize without rebuilding, wouldn't the cells need to be rebuilt to reflect the new dimensions?

The cells needs to be rebuilt due to this code:

final BoxConstraints cellConstraints = BoxConstraints.tightFor(
    width: mergedColumnWidth ?? standardColumnWidth,
    height: mergedRowHeight ?? standardRowHeight,
 );
cell.layout(cellConstraints); // this line

You can find this in the table.dart line 754 (at the moment of writing this). As you can see, the cell constraints are calculated keeping in mind the column width and then the cell is layout.

EDIT: I think what you can do is (if you use a state management library like Riverpod) is create constant widgets for each cell, for example, following the video you added in your previous comment, in the tags column, you render a

ProviderScope(
    overrides: [
        tagsProvider.overrideWithValue(/*etc etc*/)
    ],
    child: TagsColumn(),
)

widget

tomasweigenast avatar Mar 23 '24 19:03 tomasweigenast

@tomasweigenast ,I tried it and it was the right way, thanks you. And the cell paint order can override in paint.

leavky avatar Mar 25 '24 15:03 leavky

@tomasweigenast ,I tried it and it was the right way, thanks you. And the cell paint order can override in paint.

What did you do? Did you try using riverpod?

tomasweigenast avatar Mar 25 '24 17:03 tomasweigenast

@leavky it sounds like your issue may have been resolved? Can we close this then?

Piinks avatar Mar 26 '24 23:03 Piinks

@tomasweigenast ,I tried it and it was the right way, thanks you. And the cell paint order can override in paint.

What did you do? Did you try using riverpod?

yes, i use riverpod.

    return TableViewCell(
      child: ProviderScope(
        overrides: [
          tableVicinityIndexProvider.overrideWithValue((vicinity.xIndex, vicinity.yIndex)),
        ],
        child: const TableCell(),
      ),
    );

leavky avatar Mar 27 '24 01:03 leavky

@leavky it sounds like your issue may have been resolved? Can we close this then?

yes, thank you.

leavky avatar Mar 27 '24 01:03 leavky

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

github-actions[bot] avatar Apr 10 '24 04:04 github-actions[bot]