skip to Main Content

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

Answers


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

    todoTile(key: UniqueKey());
    

    or

    todoTile(key: Key('todoId'));
    
    Login or Signup to reply.
  2. 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);
            });
          },
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search