添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

How to create a reorderble tree view in Flutter? There are several packages to create tree view, but I want to make it reorderble.

This is what I want to implement.

I have no idea where to start. Can you give me just some of basic ideas?

For this flutter has a Draggable class. api.flutter.dev/flutter/widgets/Draggable-class.html Chirag Bargoojar Jan 17, 2022 at 6:17

try this package, reorderables , ReorderableWrap can be used:

class _WrapExampleState extends State<WrapExample> {
  final double _iconSize = 90;
  List<Widget> _tiles;
  @override
  void initState() {
    super.initState();
    _tiles = <Widget>[
      Icon(Icons.filter_1, size: _iconSize),
      Icon(Icons.filter_2, size: _iconSize),
      Icon(Icons.filter_3, size: _iconSize),
      Icon(Icons.filter_4, size: _iconSize),
      Icon(Icons.filter_5, size: _iconSize),
      Icon(Icons.filter_6, size: _iconSize),
      Icon(Icons.filter_7, size: _iconSize),
      Icon(Icons.filter_8, size: _iconSize),
      Icon(Icons.filter_9, size: _iconSize),
  @override
  Widget build(BuildContext context) {
    void _onReorder(int oldIndex, int newIndex) {
      setState(() {
        Widget row = _tiles.removeAt(oldIndex);
        _tiles.insert(newIndex, row);
    var wrap = ReorderableWrap(
      spacing: 8.0,
      runSpacing: 4.0,
      padding: const EdgeInsets.all(8),
      children: _tiles,
      onReorder: _onReorder,
       onNoReorder: (int index) {
        //this callback is optional
        debugPrint('${DateTime.now().toString().substring(5, 22)} reorder cancelled. index:$index');
      onReorderStarted: (int index) {
        //this callback is optional
        debugPrint('${DateTime.now().toString().substring(5, 22)} reorder started: index:$index');
    var column = Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        wrap,
        ButtonBar(
          alignment: MainAxisAlignment.start,
          children: <Widget>[
            IconButton(
              iconSize: 50,
              icon: Icon(Icons.add_circle),
              color: Colors.deepOrange,
              padding: const EdgeInsets.all(0.0),
              onPressed: () {
                var newTile = Icon(Icons.filter_9_plus, size: _iconSize);
                setState(() {
                  _tiles.add(newTile);
            IconButton(
              iconSize: 50,
              icon: Icon(Icons.remove_circle),
              color: Colors.teal,
              padding: const EdgeInsets.all(0.0),
              onPressed: () {
                setState(() {
                  _tiles.removeAt(0);
    return SingleChildScrollView(
      child: column,

Here is a full example to get the idea;

Using expandable and reorderables packages;

import 'dart:math' as math;
import 'package:expandable/expandable.dart';
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: 'Expandable Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      home: MyHomePage(),
class MyHomePage extends StatefulWidget {
  @override
  State createState() {
    return MyHomePageState();
class MyHomePageState extends State<MyHomePage> {
  List<Widget> myList = [
    ItemBuilder(title: 'Category 0'),
    ItemBuilder(title: 'Category 1'),
    ItemBuilder(title: 'Category 2'),
  @override
  Widget build(BuildContext context) {
    ScrollController _scrollController =
        PrimaryScrollController.of(context) ?? ScrollController();
    return Scaffold(
      appBar: AppBar(
        title: Text("Expandable Demo"),
      body: ExpandableTheme(
        data: const ExpandableThemeData(
          iconColor: Colors.blue,
          useInkWell: true,
        child: CustomScrollView(
          // A ScrollController must be included in CustomScrollView, otherwise
          // ReorderableSliverList wouldn't work
          controller: _scrollController,
          slivers: <Widget>[
            ReorderingItems(
              list: myList,
const loremIpsum =
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
class ItemBuilder extends StatelessWidget {
  const ItemBuilder({Key? key, required this.title}) : super(key: key);
  final String title;
  @override
  Widget build(BuildContext context) {
    buildItem(String label) {
      return Padding(
        padding: const EdgeInsets.all(10.0),
        child: Text(label),
    buildList() {
      // ScrollController _scrollController =
      //     PrimaryScrollController.of(context) ?? ScrollController();
      return Column(
        children: <Widget>[
          for (var i in [1, 2, 3, 4]) buildItem("Item ${i}"),
    return ExpandableNotifier(
        child: Padding(
      padding: const EdgeInsets.all(10),
      child: ScrollOnExpand(
        child: Card(
          clipBehavior: Clip.antiAlias,
          child: Column(
            children: <Widget>[
              ExpandablePanel(
                theme: const ExpandableThemeData(
                  headerAlignment: ExpandablePanelHeaderAlignment.center,
                  tapBodyToExpand: true,
                  tapBodyToCollapse: true,
                  hasIcon: false,
                header: Container(
                  color: Colors.indigoAccent,
                  child: Padding(
                    padding: const EdgeInsets.all(10.0),
                    child: Row(
                      children: [
                        ExpandableIcon(
                          theme: const ExpandableThemeData(
                            expandIcon: Icons.arrow_right,
                            collapseIcon: Icons.arrow_drop_down,
                            iconColor: Colors.white,
                            iconSize: 28.0,
                            iconRotationAngle: math.pi / 2,
                            iconPadding: EdgeInsets.only(right: 5),
                            hasIcon: false,
                        Expanded(
                          child: Text(
                            title,
                            style: Theme.of(context)
                                .textTheme
                                .bodyText1!
                                .copyWith(color: Colors.white),
                collapsed: Container(),
                expanded: buildList(),
class ReorderingItems extends StatefulWidget {
  const ReorderingItems({Key? key, required this.list}) : super(key: key);
  final List<Widget> list;
  @override
  State<ReorderingItems> createState() => _ReorderingItemsState();
class _ReorderingItemsState extends State<ReorderingItems> {
  late List<Widget> _rows;
  @override
  void initState() {
    super.initState();
    _rows = widget.list;
  @override
  Widget build(BuildContext context) {
    void _onReorder(int oldIndex, int newIndex) {
      setState(() {
        Widget row = _rows.removeAt(oldIndex);
        _rows.insert(newIndex, row);
    return ReorderableSliverList(
      delegate: ReorderableSliverChildListDelegate(_rows),
      onReorder: _onReorder,
                This example doesn't compile for me, what versions of the SDK and the packages are you using?
– Anakhand
                Jan 27 at 10:25
                I am using  >> expandable: ^5.0.1 reorderables: ^0.5.1 <<  Need to mention that the (reorderables 0.6.0) didn't work for me.
– Zahra
                Jan 27 at 11:09

I write simple code to give you a basic idea of how to use Drag&Drop API in a flutter. You don't need to use any third-party packages.

Here are the official docs: https://api.flutter.dev/flutter/widgets/Draggable-class.html

  • Use Draggable widget that gives us the ability to drag and move widgets around.
  • Use DragTarget widget that receives data when a Draggable widget is dropped on it.
  • Example code:

    import 'package:flutter/material.dart';
    void main() => runApp(const DragAndDropApp());
    class DragAndDropApp extends StatelessWidget {
      const DragAndDropApp({super.key});
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(title: const Text('Drag & Drop Sample')),
            body: const DragAndDropExample(),
    class DragAndDropExample extends StatefulWidget {
      const DragAndDropExample({super.key});
      @override
      State<DragAndDropExample> createState() => _MyStatefulWidgetState();
    class _MyStatefulWidgetState extends State<DragAndDropExample> {
      final _mapList = {
          'Blogs': {
            0: 'Yesterday',
            1: '2 Days ago',
          'Home': {},
          'News': {},
          'About': {},
      _reorder(Object? data) {
        debugPrint('Reorder: $data');
      @override
      Widget build(BuildContext context) {
        return ListView.builder(
          itemCount: _mapList.length,
          itemBuilder: (context, index) => Column(
            children: [
              // Topic
              _draggbleAndDragTargetListTile(
                data: {'topic': index},
                title: _mapList[index]!.keys.first,
                key: '$index',
              // Subtopics list
              ListView.builder(
                shrinkWrap: true,
                physics: const NeverScrollableScrollPhysics(),
                padding: const EdgeInsets.only(left: 32),
                itemCount: _mapList[index]!.values.first.length,
                itemBuilder: (context, subindex) => Column(
                  children: [
                    // Subtopic divider
                    _dragTargetDivider(),
                    // Subtopic
                    _draggbleAndDragTargetListTile(
                      data: {'topic': index, 'subtopic': subindex},
                      title: _mapList[index]!.values.first[subindex],
                      key: '${index}_$subindex',
              // Topic with Subtopics end divider
              _dragTargetDivider(),
      Widget _dragTargetDivider() {
        return DragTarget(
          builder: (context, acceptedItems, rejectedItems) => Divider(
            thickness: acceptedItems.isNotEmpty ? 3 : null,
            color: acceptedItems.isNotEmpty ? Colors.blue : null,
          onAccept: _reorder,
      Widget _draggbleAndDragTargetListTile({
        required Map<String, dynamic> data,
        required String title,
        required String key,
        return Draggable(
          data: data,
          feedback: Text(title),
          dragAnchorStrategy: pointerDragAnchorStrategy,
          childWhenDragging: ListTile(
            tileColor: Colors.grey,
            title: Text(title),
          child: DragTarget(
            builder: (context, acceptedItems, rejectedItems) {
              return ListTile(
                key: Key(key),
                title: Text(title),
                tileColor: acceptedItems.isNotEmpty ? Colors.blue : null,
            onAccept: _reorder,
            

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.