1

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!

bragi
  • 183
  • 2
  • 12

1 Answers1

1

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")),
                  ],
                );
              }
            }
          }),
    );
  }
}
George Rabbat
  • 538
  • 2
  • 10
  • Creating an empty one make the app possible to run, but gives this error as soon as I click the `SelectFormField`: `_AssertionError ('package:flutter/src/material/popup_menu.dart': Failed assertion: line 877 pos 10: 'items != null && items.isNotEmpty': is not true.)`. Assigning it to _items immediately gives me this error: `type 'Future' is not a subtype of type 'List>'`, which was an error I was trying to circumvent with this setup. – bragi May 07 '22 at 17:59
  • @simonbrage I have edited the answer to suit the error message. try it and give me your feedback – George Rabbat May 07 '22 at 18:40
  • Now if I assign an empty list for starters, `_items` just remains an empty list. If I assign it immediately, I also have to return `_items` in `_getData()`, but in the end I end up with this error again: `type 'Future' is not a subtype of type 'List>'` – bragi May 07 '22 at 18:51
  • @simonbrage did you tried after editing and it gives you the same error? it's impossible to be Future because of the `await` keyword. – George Rabbat May 07 '22 at 18:56
  • Ah sorry, I misread it. When I do what you suggest I get the following error in `_getDataInit()`: `_CastError (type 'Null' is not a subtype of type 'List>' in type cast)` – bragi May 07 '22 at 19:28
  • so the main error here is inside _getData() method because it return null. debug it and see what's going on there and why it returns null instead of List – George Rabbat May 07 '22 at 19:51
  • Yeah. The odd thing is e.g. if I `print(_items)` in `_getBandits()` it will have the data I'm looking for, but if I `print(_items)` in `initState()` after my `_getBandits();` call, `_items` will evaluate to `null`. – bragi May 07 '22 at 19:58
  • try to breakpoint the code and follow the code where it gets null. hit me with the result – George Rabbat May 07 '22 at 20:05
  • Hmm, if I do breakpoint at `_getData()` in `initState()` and step into it, it will show `_items` as `null` all the way through. But if I print it from `_getData()` it will output the correct data. – bragi May 07 '22 at 20:35
  • on `initState` set `print(_getData().runtimeType);` what the type it gave? – George Rabbat May 07 '22 at 20:49
  • It gives `Future` – bragi May 07 '22 at 21:15
  • in my code in the separated method (edited one) put print get data with await with runtime Type. what it yields? – George Rabbat May 07 '22 at 21:48
  • If I do `print(await _getData().runtimeType` in `_getDataInit` I get this error: **NoSuchMethodError (NoSuchMethodError: Class 'Future' has no instance getter 'runtimetype'. Receiver: Instance of 'Future' Tried calling: runtimetype)**. But I get issues with `Future` every time I try to return `_items` from `_getData()`. – bragi May 08 '22 at 10:33
  • Try wrap the code like this `print((await _getData()).runtimeType)` – George Rabbat May 08 '22 at 10:40
  • Then it prints `List>`. But it is still null when accessing it in my SelectFormField – bragi May 08 '22 at 11:16
  • I have Idea, why don't you use FutureBuilder in your build tree? isn't more efficient? If you have Google Drive please send the code at it to help you – George Rabbat May 08 '22 at 11:29
  • @bragi I've Edited the answer try it. it worked for me – George Rabbat May 08 '22 at 17:11
  • @bragi Please remove the files from Google Drive for security reasons. And change your Simply.com SQL password. And if it solved you problem accept the answer – George Rabbat May 09 '22 at 08:08
  • OMG. Thanks for that shout, was in a hurry yesterday. Your solution works perfectly, thank you very much! – bragi May 09 '22 at 08:44
  • @bragi In service. Don't forget to change SQL password – George Rabbat May 09 '22 at 08:45
  • Yep, already done. Thanks! :D – bragi May 09 '22 at 10:25