Reputation: 23
I am really newbie in Flutter and SQLite. I need to store some data got from a DB into a global variable (in this code it's a local variable just for exemplification) and I don't know:
Below is the method for the data extraction
class ObjectRepository {
ObjectRepository._();
static final ObjectRepository instance = ObjectRepository._();
Future<List<Map>> select(Database db, String tableName, {List<String> fields}) async {
sql = 'SELECT ' + (fields != null ? fields.toString() : '* ');
sql = sql + 'FROM $tableName';
final List<Map> list = await db.rawQuery(sql);
return list;
}
}
Below is where I put my future<List<Map>> into a List<Map> (I know I can't, in fact the IDE gives me this error A value of type 'Future<List<Map<dynamic, dynamic>>>' can't be assigned to a variable of type 'List<Map<dynamic, dynamic>>'. Try changing the type of the variable, or casting the right-hand type to 'List<Map<dynamic, dynamic>>'.
I even tried to cast it, but I got a similar issue at run-time "type 'Future<List<Map<dynamic, dynamic>>>' is not a subtype of type 'List<Map<dynamic, dynamic>>' in type cast".
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
void initState() {
super.initState();
DBProvider.instance.openDB(globalConstants.dbName);
List<Map> list = ObjectRepository.instance.select(DBProvider.instance.database, 'tCars') as List<Map>;
}
@override
void dispose() {
DBProvider.instance.closeDB();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(title: Text('Cars')),
drawer: CustomDrawer(),
body: CustomBody(),
);
}
}
Any idea how can I resolve it? Many thanks, folks.
Upvotes: 2
Views: 9924
Reputation: 1540
Error is because instant.select
function returns the Future<List<Map>>
but you are tring to asign it to List<Map>
therefore you have to await until the future
completes. but the initState
is not an async
function. therefore you cannot await
there. but you can use then
method on future (which is called when future is completed with the value). and give it the callback
(action you want to perform with the value) to then method. But you have to careful to check whether the list
is null or not before you using it by if
statement because it is set to null initially.
class HomePage extends StatefulWidget {
HomePage({Key key}) : super(key: key);
List<Map>? list;
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
void initState() {
super.initState();
DBProvider.instance.openDB(globalConstants.dbName);
ObjectRepository.instance.select(DBProvider.instance.database, 'tCars').then((result){
if(mounted) {
setState((){ list = result});
}else { list = result}
});
}
@override
void dispose() {
DBProvider.instance.closeDB();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(title: Text('Cars')),
drawer: CustomDrawer(),
body: CustomBody(),
);
}
}
Instant of this if you are tring to use your list for a widget (eg: ListView). consider using FutureBuilder
which takes future
and builder
function to return appropriate widget
according to the state of the future you have given to it. For Example,
FutureBuilder<List<Map>>(
future: ObjectRepository.instance.select(DBProvider.instance.database, 'tCars') // your future here,
builder : (context, snapshot) {
if (snapshot.hasError) return ErorrScreen();
if (snapshot.hasData) return ListView.builder(itemCount : snapshot.data.length,
itemBuilder: (context, index) {
return Text(snpshot.data[index].toString());
});
return CicularProgressIndicator();
}
)
this is the more recommended way of using futures in your UI in flutter.
Upvotes: 0
Reputation: 891
Reading from database is an asynchronous activity, which means the query doesn't return some data immediately. so you have to wait for the operation to complete and then assign it to a variable.
DBProvider.instance.openDB(globalConstants.dbName);
List<Map> list = await ObjectRepository.instance.select(DBProvider.instance.database, 'tCars') as List<Map>;
On the other hand, you can not use await
inside initState
.
The solution is to create an async
function that handles the process of getting data from database.
Future<List<Map>> initDB()async{
DBProvider.instance.openDB(globalConstants.dbName);
List<Map> list = await ObjectRepository.instance.select(DBProvider.instance.database, 'tCars') as List<Map>;
return list;
}
finally you can call initDB
inside initState
Upvotes: 1