admin管理员组

文章数量:1336631

Question:

I am developing a Flutter app that visualizes audio data on a timeline. Each node on the timeline corresponds to a specific position and duration of the audio. These nodes contain text and interactive buttons.

Problems:

  • The expected number of nodes per audio track is between 1,000 and 10,000.
  • The timeline needs to support smooth and fast zooming in/out.
  • All nodes must be displayed simultaneously in some cases, so excluding off-screen nodes is not an effective optimization.
  • The current implementation suffers from severe performance issues.

Attempted Solutions and Their Issues:

  1. Rebuild-Based Approach

    • Despite minimizing rebuilds, updating a large number of widgets still takes 100ms or more per frame, causing performance bottlenecks.
    • This approach is too slow to be usable.
  2. Transform-Based Approach

    • Scaling the parent widget reflects changes quickly, but child widgets are merely scaled rather than properly resized.
  3. AnimatedBuilder

    • While it allows animating size changes, it still requires rebuilding the widgets to apply updated scaling-related variables.
    • Rebuilding is necessary to set the target size of each node, making it inefficient.
  4. CustomPaint

    • Drawing nodes directly with CustomPaint is faster but makes implementing user interactions (e.g., button clicks, drag-and-drop) very challenging.
    • Additionally, this approach loses the visual benefits of Flutter widgets, such as shadows and ripple effects.

Requirements:

  1. Timeline scaling (zoom in/out) must be smooth and fast.
  2. Each node must dynamically adjust its position and size and remain interactive (e.g., clickable buttons).
  3. The app must handle 1,000 to 10,000 nodes while maintaining a 16ms frame target for smooth animations.

Why Flutter?

I chose Flutter for its cross-platform support and a rich set of pre-built widgets that accelerate development. However, I am struggling to find an efficient solution to this problem.

Questions:

  1. What are some efficient ways to solve this problem in Flutter?
  2. Is there a pattern or approach that allows managing the size and position of nodes effectively while mitigating performance issues?
  3. If using CustomPaint, how can user interactions (e.g., clicks, drags) be implemented efficiently for each node?
  4. Are there any widgets or technical alternatives that are faster than CustomPaint for this use case?

I would greatly appreciate any advice, suggestions, or alternative approaches. Thank you!

Question:

I am developing a Flutter app that visualizes audio data on a timeline. Each node on the timeline corresponds to a specific position and duration of the audio. These nodes contain text and interactive buttons.

Problems:

  • The expected number of nodes per audio track is between 1,000 and 10,000.
  • The timeline needs to support smooth and fast zooming in/out.
  • All nodes must be displayed simultaneously in some cases, so excluding off-screen nodes is not an effective optimization.
  • The current implementation suffers from severe performance issues.

Attempted Solutions and Their Issues:

  1. Rebuild-Based Approach

    • Despite minimizing rebuilds, updating a large number of widgets still takes 100ms or more per frame, causing performance bottlenecks.
    • This approach is too slow to be usable.
  2. Transform-Based Approach

    • Scaling the parent widget reflects changes quickly, but child widgets are merely scaled rather than properly resized.
  3. AnimatedBuilder

    • While it allows animating size changes, it still requires rebuilding the widgets to apply updated scaling-related variables.
    • Rebuilding is necessary to set the target size of each node, making it inefficient.
  4. CustomPaint

    • Drawing nodes directly with CustomPaint is faster but makes implementing user interactions (e.g., button clicks, drag-and-drop) very challenging.
    • Additionally, this approach loses the visual benefits of Flutter widgets, such as shadows and ripple effects.

Requirements:

  1. Timeline scaling (zoom in/out) must be smooth and fast.
  2. Each node must dynamically adjust its position and size and remain interactive (e.g., clickable buttons).
  3. The app must handle 1,000 to 10,000 nodes while maintaining a 16ms frame target for smooth animations.

Why Flutter?

I chose Flutter for its cross-platform support and a rich set of pre-built widgets that accelerate development. However, I am struggling to find an efficient solution to this problem.

Questions:

  1. What are some efficient ways to solve this problem in Flutter?
  2. Is there a pattern or approach that allows managing the size and position of nodes effectively while mitigating performance issues?
  3. If using CustomPaint, how can user interactions (e.g., clicks, drags) be implemented efficiently for each node?
  4. Are there any widgets or technical alternatives that are faster than CustomPaint for this use case?

I would greatly appreciate any advice, suggestions, or alternative approaches. Thank you!

Share asked Nov 19, 2024 at 18:53 김은수김은수 253 bronze badges 0
Add a comment  | 

1 Answer 1

Reset to default 0

Consider using the Flow Widget. It is much faster than CustomPaint. It is basically a MultiChildRenderObjectWidget. From the Documentation,

"The [Flow] widget recomputes its children's positions during the paint phase rather than during the layout phase."

Therefore, it has a significant improvement on the performance of laying out a lot of items being animated on screen. I had a similar use, where I was bubbling 150 nodes, in reaction to a moving cursor on Flutter web. The flow widget sped up this animation. You may still need some level of optimization as 10k realtime nodes is a lot more than 150 nodes.

Sample code.

Flow(delegate: PositionItemsFlowDelegate(animation: animation),
    children: List.generate(items.length,(index) => Container()),),

And the Delegate is the most important Class here. This is just a sample and doesn't highlight the beauty of Flow being used to draw a lot of nodes. The animation can be used to determine the position of items in the flow widget.

class PositionItemsFlowDelegate extends FlowDelegate {
  PositionItemsFlowDelegate({required this.animation})
      : super(repaint: animation);
  final Animation<double> animation;

  @override
  bool shouldRepaint(PositionItemsFlowDelegate oldDelegate) {
    return animation != oldDelegate.animation;
  }

  ///Just a sample that positions Items on Screen in circular fashion.
  @override
  void paintChildren(FlowPaintingContext context) {
    for (int i = 0; i < context.childCount; i++) {
      int numOfItems = context.childCount;
      int currentIndex = i;

      if (currentIndex > 0 && currentIndex < 8) {
        numOfItems = (context.childCount ~/ 5);
      } else if (currentIndex > 7 && currentIndex <= 18) {
        numOfItems = context.childCount ~/ 10;
      }
      double angle = (2 * pi / numOfItems) * currentIndex;
      double radius = min(300, currentIndex * 60);
      switch (currentIndex) {
        case 1:
          radius = 1;
        case 2:
          radius = 60;
        case 3:
          radius = 60;
          angle = angle + 0.5;
        case 4:
          radius = 60;
          angle = angle + 1;
        case 5:
          radius = 60;
          angle = angle + 1.5;
        case 0:
          radius = 240;
          angle += 3.64;
      }
      double dx = (radius * animation.value) * cos(angle);
      double dy = (radius * animation.value) * sin(angle);
      context.paintChild(
        i,
        transform: Matrix4.translationValues(dx, dy, 0),
      );
    }
  }

  @override
  Size getSize(BoxConstraints constraints) {
    return const Size.fromRadius(300);
  }

  @override
  BoxConstraints getConstraintsForChild(int i, BoxConstraints constraints) {
    return BoxConstraints.tight(const Size.fromRadius(300));
  }
}

本文标签: Efficiently Resize a Large Number of Widgets in FlutterStack Overflow