You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用两个ReorderableRow报错:ScrollController绑定多个滚动视图

问题:多ReorderableRow共存时拖拽异常

在Flutter项目中引入reorderables: ^0.5.0包实现两个可拖拽排序的ReorderableRow组件时,出现以下问题:

  • 移除第二个ReorderableRow后,第一个组件的排序功能正常
  • 同时使用两个组件时,拖拽第一个ReorderableRow内的元素会消失,并抛出断言异常:ScrollController attached to multiple scroll views

复现代码

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Drag and Drop Lists',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: OrderableRow(),
    );
  }
}

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

  @override
  State<OrderableRow> createState() => _OrderableRowState();
}

class _OrderableRowState extends State<OrderableRow> {
  late List<Widget> _listOfWidgets;
  late List<Widget> _listOfWidgets2;

  @override
  void initState() {
    super.initState();
    _listOfWidgets = <Widget>[
      Container(color: Colors.blue, key: const ValueKey(1), child: const Text('blue')),
      Container(color: Colors.red, key: const ValueKey(2), child: const Text('red')),
      Container(color: Colors.green, key: const ValueKey(3), child: const Text('green')),
    ];
    _listOfWidgets2 = <Widget>[
      Container(color: Colors.blue, key: const ValueKey(-1), child: const Text('blue')),
      Container(color: Colors.red, key: const ValueKey(-2), child: const Text('red')),
      Container(color: Colors.green, key: const ValueKey(-3), child: const Text('green')),
    ];
  }

  @override
  Widget build(BuildContext context) {
    void _onReorder(int oldIndex, int newIndex){
      setState(() {
        Widget col = _listOfWidgets.removeAt(oldIndex);
        _listOfWidgets.insert(newIndex,col);
      });
    }
    void _onReorder2(int oldIndex, int newIndex){
      setState(() {
        Widget col = _listOfWidgets2.removeAt(oldIndex);
        _listOfWidgets2.insert(newIndex,col);
      });
    }
    return Column(
      children: [
        ReorderableRow(
          onReorder: _onReorder,
          children: _listOfWidgets,
        ),
        ReorderableRow(
          onReorder: _onReorder2,
          children: _listOfWidgets2,
        ),
      ],
    );
  }
}

报错堆栈

══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
The following assertion was thrown while handling a gesture:
Assertion failed:
file:///name/development/flutter/packages/flutter/lib/src/widgets/scroll_controller.dart:108:12
_positions.length == 1
"ScrollController attached to multiple scroll views."

When the exception was thrown, this was the stack:
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 251:49  throw_
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 29:3    assertFailed
packages/flutter/src/widgets/scroll_controller.dart 108:33                    get position
packages/flutter/src/widgets/scroll_controller.dart 115:33                    get offset
packages/reorderables/src/widgets/reorderable_flex.dart 381:53                [_scrollTo]
packages/reorderables/src/widgets/reorderable_flex.dart 741:11                <fn>
packages/flutter/src/widgets/drag_target.dart 746:47                          didEnter
packages/flutter/src/widgets/drag_target.dart 902:22                          <fn>
dart-sdk/lib/collection/list.dart 161:15                                      firstWhere
packages/flutter/src/widgets/drag_target.dart 897:56                          updateDrag
packages/flutter/src/widgets/drag_target.dart 824:5                           new
packages/flutter/src/widgets/drag_target.dart 554:35                          [_startDrag]
packages/flutter/src/widgets/drag_target.dart 483:37                          <fn>
packages/flutter/src/gestures/multidrag.dart 295:53                           <fn>
packages/flutter/src/gestures/recognizer.dart 198:24                          invokeCallback
packages/flutter/src/gestures/multidrag.dart 295:14                           [_startDrag]
packages/flutter/src/gestures/multidrag.dart 285:48                           <fn>
packages/flutter/src/gestures/multidrag.dart 530:7                            [_delayPassed]
dart-sdk/lib/_internal/js_dev_runtime/private/isolate_helper.dart 48:19       internalCallback

Handler: "onStart"
Recognizer:
  DelayedMultiDragGestureRecognizer#02801
════════════════════════════════════════════════════════════════════════════════════════════════════
Another exception was thrown: Assertion failed: file:///name/development/flutter/packages/flutter/lib/src/widgets/scroll_controller.dart:108:12

问题原因

ReorderableRow组件默认会使用同一个全局ScrollController实例,当多个ReorderableRow同时存在时,就会出现一个控制器绑定多个滚动视图的情况,触发Flutter的断言检查,导致拖拽功能异常。

解决方法

为每个ReorderableRow创建独立的ScrollController实例,并通过scrollController参数传入,确保每个组件拥有专属的控制器,避免冲突。同时要记得在组件销毁时释放控制器资源。

修改后的代码

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Drag and Drop Lists',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: OrderableRow(),
    );
  }
}

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

  @override
  State<OrderableRow> createState() => _OrderableRowState();
}

class _OrderableRowState extends State<OrderableRow> {
  late List<Widget> _listOfWidgets;
  late List<Widget> _listOfWidgets2;
  // 为每个ReorderableRow创建独立的ScrollController
  late ScrollController _controller1;
  late ScrollController _controller2;

  @override
  void initState() {
    super.initState();
    _controller1 = ScrollController();
    _controller2 = ScrollController();
    _listOfWidgets = <Widget>[
      Container(color: Colors.blue, key: const ValueKey(1), child: const Text('blue')),
      Container(color: Colors.red, key: const ValueKey(2), child: const Text('red')),
      Container(color: Colors.green, key: const ValueKey(3), child: const Text('green')),
    ];
    _listOfWidgets2 = <Widget>[
      Container(color: Colors.blue, key: const ValueKey(-1), child: const Text('blue')),
      Container(color: Colors.red, key: const ValueKey(-2), child: const Text('red')),
      Container(color: Colors.green, key: const ValueKey(-3), child: const Text('green')),
    ];
  }

  @override
  void dispose() {
    // 销毁时释放控制器资源
    _controller1.dispose();
    _controller2.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    void _onReorder(int oldIndex, int newIndex){
      setState(() {
        Widget col = _listOfWidgets.removeAt(oldIndex);
        _listOfWidgets.insert(newIndex,col);
      });
    }
    void _onReorder2(int oldIndex, int newIndex){
      setState(() {
        Widget col = _listOfWidgets2.removeAt(oldIndex);
        _listOfWidgets2.insert(newIndex,col);
      });
    }
    return Column(
      children: [
        ReorderableRow(
          scrollController: _controller1, // 传入第一个控制器
          onReorder: _onReorder,
          children: _listOfWidgets,
        ),
        ReorderableRow(
          scrollController: _controller2, // 传入第二个控制器
          onReorder: _onReorder2,
          children: _listOfWidgets2,
        ),
      ],
    );
  }
}

内容的提问来源于stack exchange,提问作者Ulmer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 15:45:30