Flutter + Reorderable ListView + Sqflite instance

This post will solve the question on how to make the list formed with instance of database using Future Builder can be made reorder-able.

Sneha Saj
4 min readJul 10, 2020
https://flutterforyou.com/wp-content/uploads/2020/01/flutter-drag-to-reorder-example.gif

I assume you already have a database created. The code is for a sample note app.

Reorderable ListView

Let’s Start!

First we will make a Note Model:

class Note {
int id;
String title;
String note;
Note({this.id, this.title, this.note});

Map<String, dynamic> toMap() {
var map = Map<String, dynamic>();
map['Title'] = title;
map['Description'] = note;
return map;
}
}

Then we will have a function which gives us the NoteList. We can access this function using database helper.

Future<List<Note>> getNoteList() async {
var noteMapList = await getNoteMapList();
int count = noteMapList.length;

List<Note> noteList = List<Note>();
for (int i = 0; i < count; i++) {
Note note = Note(
id: noteMapList[i]['ID'],
title: noteMapList[i]['Title'],
note: noteMapList[i]['Description']);
noteList.add(note);
}
return noteList;
}

We will initialize the noteList and _future which would be required by future of Future Builder in the class we are going to use it.

List<Note> noteList = [];
Future<List<Note>> _future;
void initState() {
super.initState();
_future = databaseHelper.getNoteList();
}

We will build a widget. There is a list of notes with title and note body each. We have ‘index’ which acts as a key for reorderable list view and also as an index for accessing the data.

Widget build(BuildContext context) {
databaseHelper.initlizeDatabase(); // this helps us accessing the
return Scaffold( // database functions
appBar: AppBar(
backgroundColor: Colors.green,
title: Text('Message Templates'),
),
body: Container(
padding: EdgeInsets.all(8.0),
child: ListView(
children: <Widget>[
SizedBox(
height: MediaQuery.of(context).size.height * 0.882,
child: FutureBuilder(
future: _future,
builder: (BuildContext context,AsyncSnapshot
snapshot){
if (snapshot.data == null) {
return Text('Loading');
} else {
if (snapshot.data.length < 1) {
return Center(
child: Text('No Messages'),
);
}
noteList = snapshot.data;
return ReorderableListView(
children: List.generate(
snapshot.data.length,
(index) { //index is the key
return ListTile(
key: Key('$index'),
title: Text(
snapshot.data[index].title
),
),
subtitle: Text(snapshot.data[index]
.note),
onTap: () {},
),
);
},
).toList(),
onReorder: _onReorder,
);
}
}))],
)),
);
}

Now we have to define _onReorder function. Our noteList is nothing else than snapshot.data but we avoid using that to remove repetition.

void _onReorder(int oldIndex, int newIndex) async {
if (newIndex > noteList.length) newIndex = noteList.length;
if (oldIndex < newIndex) newIndex -= 1;

setState(() {
final Note item = noteList[oldIndex];
noteList.removeAt(oldIndex);

print(item.title);
noteList.insert(newIndex, item);
});
}

The above code combined:

(Note that this is the case when you already have a database_helper.dart defined and you are also inputting the values to class Note)

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);

final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
List<Note> noteList = [];
Future<List<Note>> _future;

void _onReorder(int oldIndex, int newIndex) async {
if (newIndex > noteList.length) newIndex = noteList.length;
if (oldIndex < newIndex) newIndex -= 1;

setState(() {
final Note item = noteList[oldIndex];
noteList.removeAt(oldIndex);

print(item.title);
noteList.insert(newIndex, item);
});
}

@override
void initState() {
// TODO: implement initState
super.initState();
_future = databaseHelper.getNoteList();
}

@override
Widget build(BuildContext context) {
databaseHelper.initlizeDatabase();
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.green,
title: Text('Message Templates'),
),
body: Container(
padding: EdgeInsets.all(8.0),
child: ListView(
children: <Widget>[
SizedBox(
height: MediaQuery.of(context).size.height * 0.882,
child: FutureBuilder(
future: _future,
builder: (BuildContext context,AsyncSnapshot
snapshot){
if (snapshot.data == null) {
return Text('Loading');
} else {
if (snapshot.data.length < 1) {
return Center(
child: Text('No Messages'),
);
}
noteList = snapshot.data;
return ReorderableListView(
children: List.generate(
snapshot.data.length,
(index) {
return ListTile(
key: Key('$index'),
title: Text(
snapshot.data[index].title
),
),
subtitle: Text(snapshot.data[index]
.note),
onTap: () {},
),
);
},
).toList(),
onReorder: _onReorder,
);
}
}))],
)),
);
}

The code to try reoderable list view ( directly copy paste and try):

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Note App Demo',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: MyHomePage(title: 'Message...'),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);

final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
}

class Note {
String title;
String note;

Note({this.title, this.note});
}

class databaseHelper {
static Future<List<Note>> getNoteList() {
return Future.value([
Note(title: "Hello", note: "World"),
Note(title: "Flutter", note: "Dart"),
Note(title: "Computer", note: "Science"),
Note(title: "Data", note: "Structures"),
Note(title: "Super", note: "Man")
]);
}
}

class _MyHomePageState extends State<MyHomePage> {
List<Note> noteList = [];
Future<List<Note>> _future;

void _onReorder(int oldIndex, int newIndex) async {
if (newIndex > noteList.length) newIndex = noteList.length;
if (oldIndex < newIndex) newIndex -= 1;

setState(() {
final Note item = noteList[oldIndex];
noteList.removeAt(oldIndex);

print(item.title);
noteList.insert(newIndex, item);
});
}

@override
void initState() {
// TODO: implement initState
super.initState();
_future = databaseHelper.getNoteList();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
padding: EdgeInsets.all(8.0),
child: ListView(
children: <Widget>[
SizedBox(
height: MediaQuery.of(context).size.height *0.882,
child: FutureBuilder(
future: _future,
builder: (BuildContext context, AsyncSnapshot
snapshot) {
if (snapshot.data == null) {
return Text('Loading');
} else {
if (snapshot.data.length < 1) {
return Center(
child: Text('No Messages, Create New
one'),
);
}

noteList = snapshot.data;
return ReorderableListView(
children: List.generate(
snapshot.data.length,
(index) {
return ListTile(
key: Key('$index'),
title: Text(
snapshot.data[index].title,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
subtitle:
Text(snapshot.data[index].note,
maxLines: 4),
trailing: InkWell(
child: Icon(Icons.add_box,
color: Colors.green),
onTap: () {
// addNewMessageDialog(txt);
},
),
onTap: () {},
);
},
).toList(),
onReorder: _onReorder,
);
}
}))
],
)),
);
}
}

It would look like this:

--

--

Sneha Saj

Trying to remember who I was before the world told me who to be.