I am fairly new to Flutter and am currently trying to use some data from a database to display in a dropdown select menu (using the mysql1 and select_form_field packages). The SelectFormField
needs to be passed a List<Map<String, dynamic>>
as its items:
-parameter. Whenever I try to run my code I get the exception LateError (LateInitializationError: Field '_items@68190616' has not been initialized.).
After looking around it seemed like initializing my item list in initState()
should do the trick, but I can't get that to work. Below is my main.dart.
class MyCustomFormState extends State<MyCustomForm> {
...
late List<Map<String, dynamic>> _items;
@override
void initState() {
_getData();
super.initState();
}
_getData() async {
_items = await getData();
}
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: <Widget>[
Container(
child: SelectFormField(
controller: nameController,
hintText: "...",
items: _items,
...
}
And this is my database.dart file which is able to fetch and format the data in the List<Map<String, dynamic>>
format I need:
getData() async {
List<Map<String, dynamic>> items = <Map<String, dynamic>>[];
dynamic conn = await connect('database');
var results = await conn.query('select * from data');
for (var row in results) {
items.add({'value': row[0], 'label': row[1]});
}
await conn.close();
return items;
}
Any help is greatly appreciated. Thanks!
CodePudding user response:
Try on initState() create Empty map before using it. because when enter build phase it access to null map which throw error.
@override
void initState() {
_items = <Map<String,dynamic>>[] // Empty one
_items = _getData(); // Or you can assign it immediately
super.initState();
}
Edit: Try to separate the Initiate from the initState
, Because the error that you showed me is that must be Future as await
and cast it to the required Type (in this case is List<Map<String, dynamic>>
)
@override
void initState() {
_getDataInit();
super.initState();
}
void _getDataInit() async {
_items = await _getData() as List<Map<String, dynamic>>;
}
Edit2: After investigation, It turns out there wrong Future implementation. I've add FutureBuilder
and removed initState
and _items
variable to solve the issue and here's the working code (just paste it as form.dart):
import 'database.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:select_form_field/select_form_field.dart';
import 'package:date_time_picker/date_time_picker.dart';
class MyCustomForm extends StatefulWidget {
const MyCustomForm({Key? key}) : super(key: key);
@override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
class MyCustomFormState extends State<MyCustomForm> {
final _formKey = GlobalKey<FormState>();
var nameController = TextEditingController();
var dateController = TextEditingController();
var courseController = TextEditingController();
var scoreController = TextEditingController();
final dateFormat = DateFormat('dd-MM-yyyy');
final database = Database();
@override
void dispose() {
nameController.dispose();
dateController.dispose();
courseController.dispose();
scoreController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
bool shouldDisplay = false;
return Form(
key: _formKey,
child: FutureBuilder<List<Map<String, dynamic>>>(
future: database.getBandits(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else {
if (snapshot.hasError) {
return ErrorWidget(Exception(
'Error occured when fetching data from database'));
} else if (!snapshot.hasData) {
return const Center(child: Text('Bandit is empty!'));
} else {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 30.0),
child: SelectFormField(
controller: nameController,
hintText: "...",
items: snapshot.data,
validator: (value) {
if (value == null || value.isEmpty) {
return "Hvem er du?";
}
return null;
},
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Bandit',
prefixIcon: Align(
widthFactor: 1.0,
heightFactor: 1.0,
child: Icon(Icons.person),
),
suffixIcon: Align(
widthFactor: 1.0,
heightFactor: 1.0,
child: Icon(Icons.arrow_drop_down),
)),
),
),
Container(
padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 30.0),
child: DateTimePicker(
controller: dateController,
validator: (value) {
if (value == null || value.isEmpty) {
setState(() {
dateController.text =
dateFormat.format(DateTime.now()).toString();
});
}
return null;
},
type: DateTimePickerType.date,
dateMask: 'dd-MM-yyyy',
firstDate: DateTime(2020),
lastDate: DateTime(2050),
dateLabelText: 'Dato',
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Dato',
prefixIcon: Align(
widthFactor: 1.0,
heightFactor: 1.0,
child: Icon(Icons.date_range),
)),
),
),
Container(
padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 30.0),
child: TextFormField(
controller: courseController,
validator: (value) {
if (value == null || value.isEmpty) {
setState(() {
courseController.text = "HJGK";
});
}
return null;
},
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Bane',
prefixIcon: Align(
widthFactor: 1.0,
heightFactor: 1.0,
child: Icon(Icons.golf_course),
)),
),
),
Container(
padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 30.0),
child: TextFormField(
controller: scoreController,
validator: (value) {
if (value == null || value.isEmpty) {
return "Indtast score";
}
return null;
},
keyboardType: TextInputType.number,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Score',
prefixIcon: Align(
widthFactor: 1.0,
heightFactor: 1.0,
child: Icon(Icons.sports_score)),
),
)),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
shouldDisplay = !shouldDisplay;
}
shouldDisplay
? showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("Score registreret"),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Padding(
padding:
const EdgeInsets.symmetric(
vertical: 10),
child: Text(
"Spiller: ${nameController.text}"),
),
Padding(
padding:
const EdgeInsets.symmetric(
vertical: 10),
child: Text(
"Dato: ${dateController.text}"),
),
Padding(
padding:
const EdgeInsets.symmetric(
vertical: 10),
child: Text(
"Bane: ${courseController.text}"),
),
Padding(
padding:
const EdgeInsets.symmetric(
vertical: 10),
child: Text(
"Score: ${scoreController.text}"),
),
],
),
),
actions: <Widget>[
TextButton(
child: const Text('OK'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
)
: null;
},
child: const Text("Submit")),
],
);
}
}
}),
);
}
}