Flutter reorderable list

153 Views Asked by At

I am trying to create a simple Todo app. Working on the UI first so it's all dumb data, not user input at this point.

I can get it working fine with list view, but when I try with reorderable list, it's suddenly harder (because of keys etc). I want to keep my tiles in a separate file to ensure the code is easier to read too.

My code so far consists of the following:

My main.dart file

import 'package:flutter/material.dart';
import 'package:todo/tiles.dart';

void main() {
  runApp(MaterialApp(
    home: TodoApp(),
    theme: ThemeData(primarySwatch: Colors.yellow),
  ));
}

class TodoApp extends StatelessWidget {
  TodoApp({super.key});

  final List fullList = [
    const todoTile(),
    const todoTile(),
    const todoTile(),
    const todoTile(),
  ];

  @override
  Widget build(context) {
    return Scaffold(
      backgroundColor: Colors.yellow[100],
      appBar: AppBar(
        title: const Text("Todo"),
        centerTitle: true,
        elevation: 0,
      ),
      body: Padding(
          padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 15.0),
          child: Column(
            children: [
              Expanded(
                child: ReorderableListView(
                  onReorder: (oldIndex, newIndex) {},
                  children: fullList,
                ),
              ),
            ],
          )),
    );
  }
}

My tiles.dart file



import 'package:flutter/material.dart';

class todoTile extends StatefulWidget {

  const todoTile({super.key});

  @override

  State<todoTile> createState() => todoTileState();

}

class todoTileState extends State<todoTile> {

  bool checkedValue = false;

  @override

  Widget build(BuildContext context) {

    return Padding(

      padding: const EdgeInsets.symmetric(vertical: 8.0),

      child: Container(

        decoration: BoxDecoration(

            borderRadius: BorderRadius.circular(10),

            color: checkedValue ? Colors.grey[350] : Colors.yellow),

        child: Padding(

          padding: const EdgeInsets.all(8.0),

          child: CheckboxListTile(

            activeColor: Colors.black,

            controlAffinity: ListTileControlAffinity.leading,

            title: Text(

              'perform an exorcism',

              style: TextStyle(

                decoration: checkedValue

                    ? TextDecoration.lineThrough

                    : TextDecoration.none,

              ),

            ),

            value: checkedValue,

            onChanged: (newCheckedValue) {

              setState(() {

                checkedValue = newCheckedValue!;

              });

            },

          ),

        ),

      ),

    );

  }

}


I initially had it working with just the ListView, but now want to try with reorderable list. I'm a complete noob to flutter so this is how far I got!

2

There are 2 best solutions below

0
On

Like @Soliev's answer, each item in ReorderableListView must have Key for identification. Therefore, you should identify Key for it, so ValueKey or ObjectKey will be more effective

You can take a look to ReorderableListView's documentation for more information about this.

Please be aware of that All list items must have a key. (Quote from documentation).

Here the sample of ReorderableListView from Flutter's example:

import 'package:flutter/material.dart';

/// Flutter code sample for [ReorderableListView].

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

class ReorderableApp extends StatelessWidget {
  const ReorderableApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('ReorderableListView Sample')),
        body: const ReorderableExample(),
      ),
    );
  }
}

class ReorderableExample extends StatefulWidget {
  const ReorderableExample({super.key});

  @override
  State<ReorderableExample> createState() => _ReorderableListViewExampleState();
}

class _ReorderableListViewExampleState extends State<ReorderableExample> {
  final List<int> _items = List<int>.generate(50, (int index) => index);

  @override
  Widget build(BuildContext context) {
    final ColorScheme colorScheme = Theme.of(context).colorScheme;
    final Color oddItemColor = colorScheme.primary.withOpacity(0.05);
    final Color evenItemColor = colorScheme.primary.withOpacity(0.15);

    return ReorderableListView(
      padding: const EdgeInsets.symmetric(horizontal: 40),
      children: <Widget>[
        for (int index = 0; index < _items.length; index += 1)
          ListTile(
            key: Key('$index'),
            tileColor: _items[index].isOdd ? oddItemColor : evenItemColor,
            title: Text('Item ${_items[index]}'),
          ),
      ],
      onReorder: (int oldIndex, int newIndex) {
        setState(() {
          if (oldIndex < newIndex) {
            newIndex -= 1;
          }
          final int item = _items.removeAt(oldIndex);
          _items.insert(newIndex, item);
        });
      },
    );
  }
}
1
On

You need to have keys for each item in the List. You can do

todoTile(key: UniqueKey());

or

todoTile(key: Key('todoId'));