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

When my mouse hovers over the Divider, it refreshes all the areas, which causes some areas to have abnormal states. #65

Closed leavky closed 2 weeks ago

leavky commented 1 month ago

First of all, thank you very much for creating this package. When my mouse hovers over the Divider, it refreshes all the areas, which causes some areas to have abnormal states. like this: 11

On the other hand, creating split views similar to those in vscode is not feasible, and I think this could be taken as a reference. https://github.com/nank1ro/flutter-shadcn-ui/blob/main/lib/src/components/resizable.dart image

caduandrade commented 1 month ago

Hi @leavky!

Could you change the example to generate the same problem reported? I believe you are rebuilding the state in your application somewhere you shouldn't be.

In the example, each area has a Widget with state and this is not lost when moving the mouse over the divider.

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

void main() => runApp(const MultiSplitViewExampleApp());

class MultiSplitViewExampleApp extends StatelessWidget {
  const MultiSplitViewExampleApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MultiSplitViewExample(),
    );
  }
}

class MultiSplitViewExample extends StatefulWidget {
  const MultiSplitViewExample({Key? key}) : super(key: key);

  @override
  MultiSplitViewExampleState createState() => MultiSplitViewExampleState();
}

class MultiSplitViewExampleState extends State<MultiSplitViewExample> {
  final MultiSplitViewController _controller = MultiSplitViewController();

  @override
  void initState() {
    super.initState();
    _controller.areas = [
      Area(builder: (c, a) => const MyStatefulWidget()),
      Area(builder: (c, a) => const MyStatefulWidget())
    ];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: const Text('Multi Split View Example')),
        body: MultiSplitViewTheme(
            data: MultiSplitViewThemeData(
                dividerPainter: DividerPainters.grooved2()),
            child: MultiSplitView(controller: _controller))
        // body: horizontal,
        );
  }
}

class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => MyState();
}

class MyState extends State<MyStatefulWidget> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Center(
        child: ElevatedButton(onPressed: _incCount, child: Text('$_count')));
  }

  void _incCount() {
    setState(() {
      _count++;
    });
  }
}
caduandrade commented 1 month ago

I don't understand what is not possible related to vscode. Do you mean the column of lines of code? You could create an area with a fixed width but in this case, I think a Row would be enough.

leavky commented 1 month ago

Thank you reply, after test i found the problem, when child params is parent params, the child can rebuild.

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

void main() => runApp(const MultiSplitViewExampleApp());

class MultiSplitViewExampleApp extends StatelessWidget {
  const MultiSplitViewExampleApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MultiSplitViewExample(
        title: "love love",
      ),
    );
  }
}

class MultiSplitViewExample extends StatefulWidget {
  const MultiSplitViewExample({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  MultiSplitViewExampleState createState() => MultiSplitViewExampleState();
}

class MultiSplitViewExampleState extends State<MultiSplitViewExample> {
  final MultiSplitViewController _controller = MultiSplitViewController();

  @override
  void initState() {
    super.initState();
    _controller.areas = [Area(builder: (c, a) => const MyStatefulWidget(title: "one")), Area(builder: (c, a) => MyStatefulWidget(title: widget.title))];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: const Text('Multi Split View Example')),
        body: MultiSplitViewTheme(data: MultiSplitViewThemeData(dividerPainter: DividerPainters.grooved2()), child: MultiSplitView(controller: _controller))
        // body: horizontal,
        );
  }
}

class MyStatefulWidget extends StatefulWidget {
  const MyStatefulWidget({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<StatefulWidget> createState() => MyState();
}

class MyState extends State<MyStatefulWidget> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    print("MyStatefulWidget build");
    return Center(child: ElevatedButton(onPressed: _incCount, child: Text('${widget.title} $_count')));
  }

  void _incCount() {
    setState(() {
      _count++;
    });
  }
}
leavky commented 4 weeks ago

@caduandrade Is there a solution, please?

rodolfogoulart commented 2 weeks ago

Same problem for me. When the mouse is houver the divider, the Area rebuid again, even if not changed the size.

caduandrade commented 2 weeks ago

Hey guys! @leavky and @rodolfogoulart, in fact the areas are rebuilt but this is expected ok? Hover changes the state of the component, causing it to rebuild itself. This happens to change the cursor, apply animation, etc. But this is not a problem as you can see in the example, the states of the areas remain the same. Flutter is very efficient at this. This rebuild is irrelevant to performance.

@leavky, you must have a problem with the state of your component. Note that using a setState in the parent widget should not affect the state of your widget. Maybe you are implementing code out of state.

caduandrade commented 2 weeks ago

@leavky,

Try isolating your tree. In this example, the tree would probably also expand again when clicking the parent's "rebuild" button.

import 'package:flutter/material.dart';

void main() => runApp(const ExampleApp());

class ExampleApp extends StatelessWidget {
  const ExampleApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: const Scaffold(body: ParentWidget()));
  }
}

class ParentWidget extends StatefulWidget {
  const ParentWidget({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => ParentWidgetState();
}

class ParentWidgetState extends State<ParentWidget> {
  @override
  Widget build(BuildContext context) {
    print("Rebuilding parent...");
    return Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: [
      Center(
          child: ElevatedButton(
              onPressed: _rebuild, child: const Text('Rebuild'))),
      // not using const to rebuild
      Expanded(child: MyWrongStatefulWidget())
    ]);
  }

  void _rebuild() {
    setState(() {});
  }
}

class MyWrongStatefulWidget extends StatefulWidget {
  const MyWrongStatefulWidget({Key? key}) : super(key: key);

  @override
  State<StatefulWidget> createState() => MyWrongState();
}

class MyWrongState extends State<MyWrongStatefulWidget> {
  bool _expanded = false;

  @override
  void didUpdateWidget(covariant MyWrongStatefulWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    // its wrong
    _expanded = false;
  }

  @override
  Widget build(BuildContext context) {
    print('build');
    return Center(
        child: ElevatedButton(
            onPressed: _onClick, child: Text('Expanded: $_expanded')));
  }

  void _onClick() {
    setState(() {
      _expanded = !_expanded;
    });
  }
}
rodolfogoulart commented 2 weeks ago

@caduandrade (foi mal meu ingles kkkkkk, vi que tu é BR tbm depois, mas vou colocar em ingles pra ficar para todos)

in fact the areas are rebuilt but this is expected ok?

I think that is not necessary to rebuild the whole area just because a houver on the divider, the divider animations, cursor, etc, can be managed on a separeted state. On my case, i have some areas that use a FutureBuilder, this was causing the area to trigger another build and to call a future function.

I made a pull request with this changes...

caduandrade commented 2 weeks ago

@rodolfogoulart, thanks for the contribution! Valeu! Ficou ótimo.

@leavky, maybe @rodolfogoulart 's merge request will solve your problem.

I'll publish the new version by tomorrow, ok?

leavky commented 2 weeks ago

@rodolfogoulart, thanks for the contribution! Valeu! Ficou ótimo.

@leavky, maybe @rodolfogoulart 's merge request will solve your problem.

I'll publish the new version by tomorrow, ok?

Thanks~

caduandrade commented 2 weeks ago

Version 3.2.0 released.

Tks guys!