caduandrade / multi_split_view

Provides horizontal or vertical multiple split view for Flutter.
https://caduandrade.github.io/multi_split_view/
MIT License
129 stars 24 forks source link

onDividerDragUpdate not changing the width in realtime #63

Closed Uche01 closed 1 month ago

Uche01 commented 1 month ago

I am saving the area of the the MultiSplitView using provider onDrag such that widget is rebuilt on drag to accommodate change in area. However, dragging the divider is not smooth, it rather occurs in step. Here is my code in onDividerDragUpdate:

onDividerDragUpdate: (index) {
  double? flex1 = hMainAxisController.areas[0].flex;
  double? flex2 = hMainAxisController.areas[1].flex;
  double? min1 = hMainAxisController.areas[0].min;
  double? min2 = hMainAxisController.areas[1].min;
  double? max1 = hMainAxisController.areas[0].max;
  double? max2 = hMainAxisController.areas[1].max;
  Area area1 = Area(flex: flex1, min: min1, max: max1);
  Area area2 =Area(flex: flex2, min: min2, max: max2);
  context.read<LeftPaneState>().hAxisMainAreas = [area1, area2];
},

in v2, I used onWeightChanged to achieve this, however, this doesn't listen for weight changes in realtime, only after user releases the hold on the divider. In my use case, I want changes to happen as user drags the divider

caduandrade commented 1 month ago

Notifications are triggered within Flutter's onVerticalDragUpdate and onHorizontalDragUpdate events. They are not fired on onVerticalDragEnd and onHorizontalDragEnd.

Could you try the "Divider - Listener" demo from https://caduandrade.github.io/multi_split_view_demo/?

Uche01 commented 1 month ago

Hi @caduandrade, I have checked the demo. I need to modify the controller's area (via flex property) as the divider is dragged. The new area is retrieved using controller.areas property. However, when I do this, the divider is unable to get dragged in a continuous manner, rather it happens in discrete steps which appears to be laggy.

To reproduce this:

  1. Create the sections using controllers
    MultiSplitViewController controller =
        MultiSplitViewController(areas: [Area(flex: 1, widget: Container()), Area(flex: 2, widget: Container())]);
  2. Create the MultiSplitView and add onDividerDragUpdate property
    child: MultiSplitView(
    axis: Axis.horizontal,
    controller: controller,
    onDividerDragUpdate: (index) {
    // get the new areas here and store the updated areas in a provider. This will lead to changes in some of the app's UI
    List<Area> updatedAreas = controller.areas;
    state.areas = updatedAreas; // this is my case but you can reproduce with: controller.areas = updatedAreas;
    },
    ),

PS: This drag is smooth when I remove the state.areas = updatedAreas or controller.areas = updatedAreas. However, removing them is undesired as the they trigger a change in my UI that is necessary

caduandrade commented 1 month ago

Hi @Uche01!

I changed the example as you suggested:

In initState:

    _controller.areas = [
      Area(flex: 1, widget: Container(color: Colors.blue)),
      Area(flex: 2, widget: Container(color: Colors.green))
    ];

and:

_onDividerDragUpdate(int index) {    
    List<Area> updatedAreas = _controller.areas;
    _controller.areas = updatedAreas;
  }

This didn't work because when changing areas, the drag event was canceled to avoid problems as it could also have a different number of areas. But maybe if the developer wants to do this during drag, that's their responsibility and right. I imagine this is your case.

But before I generate any version, the best thing would be for you to test it by changing the multi_split_view locally. You could clone the local project and point your project to the clone. It would look something like this:

dependencies:
  flutter:
    sdk: flutter
  multi_split_view: #^3.0.2
    path: ../multi_split_view/

So there are 2 points that you can change on the MultiSplitView class to try it out.

The first is where you cancel the drag. The build method. Stop overriding the _draggingDivider. Would be like this:

      if (_lastAreasUpdateHash != controllerHelper.areasUpdateHash ||
          _layoutConstraints.containerSize != containerSize ||
          _layoutConstraints.areasCount != _controller.areasCount ||
          _layoutConstraints.dividerThickness != themeData.dividerThickness) {
        //_draggingDivider = null;

The second point that you could perhaps test is to immediately notify the listener of the dragging update at the end of the _onDragUpdate method. If you notice, it currently looks like this:

Future.delayed(Duration.zero, () => widget.onDividerDragUpdate!(index));

But it could be like this:

widget.onDividerDragUpdate!(index);

Note that this was a guarantee that the size/flex would already be changed and visible when the listener was notified. I believe it should not be modified but it is worth testing.

Could you do this test and let me know?

Uche01 commented 1 month ago

Hello @caduandrade, thanks for your response. Using //_draggingDivider = null; worked and the widget is now freely draggable.