Why I am getting Unhandled Exception: NoSuchMethodError: The method 'toMap' was called on null this error. I stuck here but I can't understand my mistake. I think the instance of model class is not getting but Iknow why it is happening.
Model Class
class AddressModel {
int _id;
String _pin;
AddressModel(this._pin);
int get id => _id;
set id(int value) {
this._id = value;
}
Map<String, dynamic> toMap() {
var map = Map<String, dynamic>();
if (_id != null) {
map['id'] = _id;
}
map['pincode'] = _pin;
return map;
}
String get pin => _pin;
set pin(String value) {
this._pin = value;
}
}
Database
class DatabaseHelper{
DatabaseHelper();
static final DatabaseHelper db = DatabaseHelper();
Database _database;
String addressTable = 'address';
String addressId = 'id';
String pinCodeColumn = 'pincode';
Future<Database> get database async {
if (_database != null) return _database;
_database = await getDatabaseInstance();
return _database;
}
Future<Database> getDatabaseInstance() async {
Directory directory = await getApplicationDocumentsDirectory();
String path = join(directory.path, "student.db");
return await openDatabase(path, version: 1,onCreate: _createDb);
}
void _createDb(Database db, int newVersion) async {
await db.execute('CREATE TABLE $addressTable ($addressId INTEGER PRIMARY KEY AUTOINCREMENT, $pinCodeColumn TEXT)');
}
Future<int> insertData(AddressModel note) async {
Database db = await this.database;
var result = await db.insert(addressTable, note.toMap());
return result;
}
}
Main Class
class _MyHomePageState extends State<MyHomePage> {
AddressModel model;
TextEditingController pinCode = TextEditingController();
DatabaseHelper helper = DatabaseHelper();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 15.0, bottom: 15.0),
child: TextField(
keyboardType: TextInputType.phone,
controller: pinCode,
onChanged: (value) {
debugPrint('Something changed in Title Text Field');
updatePin();
},
decoration: InputDecoration(
labelText: 'Pin Code *',
hintText: 'Fill your Pin Code',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0))),
),
),
RaisedButton(
onPressed: (){
helper.insertData(model);
},
)
],
),
),
);
}
void updatePin(){
model.pin = pinCode.text;
}
}
This is the error
E/flutter (31607): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The method 'toMap' was called on null.
E/flutter (31607): Receiver: null
E/flutter (31607): Tried calling: toMap()
E/flutter (31607): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
E/flutter (31607): #1 DatabaseHelper.insertProject (package:xpecto_teasoko/databasee.dart:50:53)
Your object of AddressModel has never been initialized in _MyHomePageState, it has just been declared. You need to initialize it if you want to use that object. You can do that this way.
class _MyHomePageState extends State<MyHomePage> {
AddressModel model = AddressModel("PIN");
...
}
Related
I'm having this super annoying issue of being unable to grab and display a table from my server hosted on PhpmyAdmin. (I've managed to grab the data and have it printed in the console, but now that I'm trying to display it in a table I can't seem to get it working)
I've tried nulling my variables but I'm not really sure what the main culprit for this error is. Any help would be greatly appreciated.
Image of Error
data.dart File
class dataListing extends StatefulWidget {
const dataListing({Key? key}) : super(key: key);
#override
State<dataListing> createState() => _dataListingState();
}
class _dataListingState extends State<dataListing> {
#override
Widget build(BuildContext context) {
return Container();
}
}
class listingData{
String? ListingID, listingName, listingDescription, address, suburbName, phoneNumber, openingHours, Email, Website;
listingData({
this.ListingID,
this.listingName,
this.listingDescription,
this.address,
this.suburbName,
this.phoneNumber,
this.openingHours,
this.Email,
this.Website,
});
//constructor
List<listingData> datalist = [];
factory listingData.fromJSON(Map<String, dynamic> json){
return listingData(
ListingID: json["ListingID"],
listingName: json["listingName"],
listingDescription: json["listingDescription"],
address: json["address"],
suburbName: json["suburbName"],
phoneNumber: json["phoneNumber"],
openingHours: json["openingHours"],
Email: json["Email"],
Website: json["Website"],
);
}
}
Directory.dart file
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:app/pages/data.dart';
class directoryPage extends StatefulWidget {
#override
State<directoryPage> createState() => _directoryPageState();
}
class _directoryPageState extends State<directoryPage> {
// List serviceListing = [];
//
// getAllListing()async{
// String url = "URL HERE";
// var response = await http.get(Uri.parse(url));
// if (response.statusCode == 200){
// setState (() {
// serviceListing = json.decode(response.body);
// });
// print (serviceListing);
// return serviceListing;
// }
// }
bool error = false, dataloaded = false;
var data;
String dataurl = "URL HERE";
#override
void initState (){
loaddata();
super.initState();
// getAllListing();
}
void loaddata() {
Future.delayed(Duration.zero,() async {
var res = await http.post(Uri.parse(dataurl));
if (res.statusCode == 200) {
setState(() {
data = json.decode(res.body);
dataloaded = true;
});
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Directory'),
centerTitle: true,
elevation: 0,
backgroundColor: Color(0xFFA30B32),
//WSU Appbar Icon
leading: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset("assets/wsulogo.png", scale: 8.0),
),
),
body: Container(
padding: EdgeInsets.all(15),
child:dataloaded?datalist():
Center(
child:CircularProgressIndicator()
),
)
);
}
Widget datalist(){
if(data["error"]) {
return Text(data["errmsg"]);
}else{
List<listingData> datalist = List<listingData>.from(data["data"].map((i){
return listingData.fromJSON(i);
})
);
return Table( //if data is loaded then show table
border: TableBorder.all(width:1, color:Colors.black45),
children: datalist.map((listingdata){
return TableRow( //return table row in every loop
children: [
//table cells inside table row
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.ListingID!)
)
),
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.listingName!)
)
),
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.listingDescription!)
)
),
TableCell(child: Padding(
padding: EdgeInsets.all(5),
child:Text(listingdata.address!)
)
),
]
);
}).toList(),
);
}
}
}
Looks like the issue was actually unrelated to the dart side of things, the php code wasn't properly structuring the data. Cannot have underscores or spaces.
Correct-> $json["dballlisting"] = array (); (I renamed it to just "data" later)
Incorrect->$json["db_all_listing"] = array ();
The error seems to be originating from this line, the data['data'] is null which is expected to be an Array.
List<listingData> datalist = List<listingData>.from(data["data"].map((i){
return listingData.fromJSON(i);
})
You need to investigate your API call to make sure why it is happening. If the null value is expected then you need to add safeguards in your code to make sure it won't break when it encounter such scenarios. You can add null safety checks for that one way to do it would be to
List<listingData> datalist = List<listingData>.from((data["data"] ?? []).map((i){
return listingData.fromJSON(i);
})
I need to list a table in a .db database. When I run the application in the emulator, the error "The getter 'length' was called on null" pops up for a second, but then the list I need is displayed immediately.
And when you start Debug on a connected smartphone, everything stops with the error "The getter 'length' was called on null".
What could be the problem? It seems that somewhere there is not enough method of waiting for data from the database.
I/flutter (10923): /data/user/0/com.example.test_project/databases/database.db
I/flutter (10923): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY
╞═══════════════════════════════════════════════════════════
I/flutter (10923): The following NoSuchMethodError was thrown building
FutureBuilder<List<Authors>>(dirty, state:
I/flutter (10923): _FutureBuilderState<List<Authors>>#3ecfd):
I/flutter (10923): The getter 'length' was called on null.
I/flutter (10923): Receiver: null
I/flutter (10923): Tried calling: length
Database.dart
class DBProvider {
DBProvider._();
static final DBProvider db = DBProvider._();
Database _database;
Future<Database> get database async {
_database = await initDB();
return _database;
}
initDB() async {
String path = join(await getDatabasesPath(), "database.db");
var exists = await databaseExists(path);
print(path);
return await openDatabase(path);
}
Future<List<Authors>> getAllClients() async {
final db = await database;
var res = await db.query('category');
print(res);
List<Authors> list = [];
list = res.map((c) => Authors.fromMap(c)).toList();
return list;
}
}
This is the class where the UI of the sheet of elements from the database is drawn.
class MainTab extends StatefulWidget {
#override
_MainTabState createState() => _MainTabState();
}
class _MainTabState extends State<MainTab> {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 10, right: 10, bottom: 10),
child: Container(
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Theme.of(context).accentColor,
borderRadius: BorderRadius.all(Radius.circular(30))
),
child: FutureBuilder<List<Authors>>(
future: DBProvider.db.getAllClients(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
Authors item = snapshot.data[index];
return ListTile(
title: Text(item.name),
leading: Icon(Icons.folder),
trailing: Text(item.count.toString()),
onTap: () {
},
);
},
);
},
)
),
);
}
}
Use ConnectionState
https://pub.dev/documentation/flutter_for_web/latest/widgets/FutureBuilder-class.html
You have to write something like this:
FutureBuilder<List<Authors>>(
future: DBProvider.db.getAllClients(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
case ConnectionState.done:
{
if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
} else if (snapshot.hasData) {
return ListView.builder(
itemCount:snapshot.data==null?0: snapshot.data.length,
itemBuilder: (context, index) {
Authors item = snapshot.data[index];
return ListTile(
title: Text(item.name),
leading: Icon(Icons.folder),
trailing: Text(item.count.toString()),
onTap: () {
},
);
}
return Center(child: Text('No Data'));
}
default:
return Container();
}
}),
I'm trying to get data from sqlite database using sqflite and insert it in list_view. Anything seems fine but when I add something to the database and try to get the data and put it in a list view in home.dart file, I keep getting an error.
Below is the home.dart file. This is where I'm trying to get data from database and make a list view.
The error occurs on this file.
import 'package:com.example.simple_app/models/person.dart';
import 'package:com.example.simple_app/utils/db_helper.dart';
import 'package:flutter/material.dart';
import 'package:com.example.simple_app/common_widgets/appbar.dart';
import 'package:com.example.simple_app/common_widgets/drawer.dart';
import 'dart:async';
import 'package:sqflite/sqflite.dart';
Future<List<Person>> getPersonsFromDB() async {
var dbHelper = DBHelper();
Future<List<Person>> persons = dbHelper.getPersons();
return persons;
}
class HomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _HomePageState();
}
}
class _HomePageState extends State<HomePage> {
int _currentIndex = 0;
final List<Widget> _children = [];
static const TextStyle moneyLendingAction =
TextStyle(color: Colors.white, fontSize: 20);
final controller_name = new TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: appbar(context, 'Simple App', 'other data'),
body: new Container(
padding: EdgeInsets.all(16.0),
child: FutureBuilder<List<Person>>(
future: getPersonsFromDB(),
builder: (context, snapshot) {
print(snapshot.data);
if (snapshot.data != null) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return new Row(
children: <Widget>[
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
padding: const EdgeInsets.only(
bottom: 8.0,
),
child: Text(
snapshot.data[index].name,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
],
),
),
],
);
});
}
}
// show loading
// return new Container(
// alignment: AlignmentDirectional.center,
// child: new CircularProgressIndicator(),
// );
}),
),
);
}
setState(() {
_currentIndex = index;
});
}
}
Below is the db_helper.dart file.
import 'dart:async';
import 'dart:io' as io;
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:com.example.simple_app/models/person.dart';
import 'package:path/path.dart';
class DBHelper {
static Database db_instance;
Future<Database> get db async {
if (db_instance == null) db_instance = await initDB();
return db_instance;
}
initDB() async {
var databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'demo2.db');
var db = await openDatabase(path, version: 1, onCreate: onCreateFunc);
return db;
}
void onCreateFunc(Database db, int version) async {
// create table
await db.execute(
'CREATE TABLE person(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, created DATETIME, updated DATETIME);');
}
/**
* crud functions
*/
Future<List<Person>> getPersons() async {
var db_connection = await db;
List<Map> list = await db_connection.rawQuery('SELECT * FROM person');
List<Person> persons = new List();
for (int i = 0; i < list.length; i++) {
Person person = new Person();
person.id = list[i]['id'];
person.name = list[i]['name'];
person.created = list[i]['created'];
person.updated = list[i]['updated'];
persons.add(person);
}
return persons;
}
// add new person
void addNewPerson(Person person) async {
var db_connection = await db;
String query = """
INSERT INTO person(name,created,updated) VALUES('${person.name}','${person.created}', '${person.updated}')
""";
await db_connection.transaction((transaction) async {
return await transaction.rawInsert(query);
});
}
// update person
void updatePerson(Person person) async {
var db_connection = await db;
String query = """
UPDATE person SET name='${person.name}',created='${person.created}',updated='${person.updated}' WHERE id=${person.id}
""";
await db_connection.transaction((transaction) async {
return await transaction.rawQuery(query);
});
}
// delete person
void deletePerson(Person person) async {
var db_connection = await db;
String query = """
DELETE FROM person WHERE id=${person.id}
""";
await db_connection.transaction((transaction) async {
return await transaction.rawQuery(query);
});
}
}
Below is the error log I'm getting in the debug console.
I/flutter (12326): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (12326): The following assertion was thrown building FutureBuilder<List<Person>>(dirty, state:
I/flutter (12326): _FutureBuilderState<List<Person>>#caa58):
I/flutter (12326): A build function returned null.
I/flutter (12326): The offending widget is:
I/flutter (12326): FutureBuilder<List<Person>>
I/flutter (12326): Build functions must never return null.
I/flutter (12326): To return an empty space that causes the building widget to fill available room, return
I/flutter (12326): "Container()". To return an empty space that takes as little room as possible, return
// and the logs go on and on below...
What I don't understand is anything seems fine but when I try to get data from sqlite database and list the data in Home Page file I keep getting an error. This may be a simple problem but because I'm new to flutter I'm failing to figure this out.
What I want is to be able to get the data from the database and put it in a list view in Home Page without getting any error. Thank you, Posted with love.
As stated, Build functions must never return null. Simply uncomment the code you have commented which will be called before the future completes:
// show loading
return new Container(
alignment: AlignmentDirectional.center,
child: new CircularProgressIndicator(),
);
I'm configuring sqflite into my app so that I can be able to perform crud operations. I've been working on this for days.
Everything seems fine but when I try to load a widget in routes so that I can try to experiment crud operations I keep getting errors relating to arguments when calling a widget class.
This may be a simple problem but because I'm new to flutter and dart I failing to figure this out.
Below is the main.dart file.
import 'package:flutter/material.dart';
import 'home.dart';
import 'package:com.example.simple_app/pages/create_account/create_account_page.dart';
import 'package:com.example.simple_app/pages/add_person/add_person.dart';
import 'package:com.example.simple_app/models/user.dart';
void main() => runApp(SimpleApp());
final routes = {
'/': (BuildContext context) => new CreateAccountPage(),
class SimpleApp extends StatefulWidget {
#override
_SimpleAppState createState() => _SimpleAppState();
}
class _SimpleAppState extends State<SimpleApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Simple App',
theme: ThemeData(
primarySwatch: Colors.blueGrey,
),
initialRoute: '/',
routes: routes,
);
}
}
Below is the create_account_page.dart file
import 'package:flutter/material.dart';
import 'package:com.example.money_lender_app/models/user.dart';
class CreateAccountPage extends StatefulWidget {
final User user;
CreateAccountPage(this.user);
#override
CreateAccountPageState createState() => CreateAccountPageState(this.user);
}
//class controller
class CreateAccountPageState extends State<CreateAccountPage> {
User user;
CreateAccountPageState(this.user);
TextEditingController nameController = TextEditingController();
#override
Widget build(BuildContext context) {
//kondisi
if (user != null) {
nameController.text = user.name;
}
//rubah
return Scaffold(
appBar: AppBar(
title: user == null ? Text('Tambah') : Text('Rubah'),
leading: Icon(Icons.keyboard_arrow_left),
),
body: Padding(
padding: EdgeInsets.only(top: 15.0, left:10.0, right:10.0),
child: ListView(
children: <Widget> [
// nama
Padding (
padding: EdgeInsets.only(top:20.0, bottom:20.0),
child: TextField(
controller: nameController,
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: 'Nama Lengkap',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
onChanged: (value) {
//
},
),
),
// tombol button
Padding (
padding: EdgeInsets.only(top:20.0, bottom:20.0),
child: Row(
children: <Widget> [
// tombol simpan
Expanded(
child: RaisedButton(
color: Theme.of(context).primaryColorDark,
textColor: Theme.of(context).primaryColorLight,
child: Text(
'Save',
textScaleFactor: 1.5,
),
onPressed: () {
// current datetime
var currentDate = new DateTime.now();
if (user == null) {
// tambah data
user = User(nameController.text, currentDate, currentDate);
} else {
// ubah data
user.name = nameController.text;
user.created_at = currentDate;
user.updated_at = currentDate;
}
// kembali ke layar sebelumnya dengan membawa objek user
Navigator.pop(context, user);
},
),
),
Container(width: 5.0,),
// tombol batal
Expanded(
child: RaisedButton(
color: Theme.of(context).primaryColorDark,
textColor: Theme.of(context).primaryColorLight,
child: Text(
'Cancel',
textScaleFactor: 1.5,
),
onPressed: () {
Navigator.pop(context);
},
),
),
],
),
),
],
),
)
);
}
}
below is the db_helper.dart file.
import 'package:sqflite/sqflite.dart';
import 'dart:async';
//mendukug pemrograman asinkron
import 'dart:io';
//bekerja pada file dan directory
import 'package:path_provider/path_provider.dart';
import 'package:com.example.simple_app/models/user.dart';
//pubspec.yml
class DbHelper {
static DbHelper _dbHelper;
static Database _database;
DbHelper._createObject();
factory DbHelper() {
if (_dbHelper == null) {
_dbHelper = DbHelper._createObject();
}
return _dbHelper;
}
Future<Database> initDb() async {
Directory directory = await getApplicationDocumentsDirectory();
String path = directory.path + 'simpleapp.db';
//create, read databases
var todoDatabase = openDatabase(path, version: 1, onCreate: _createDb);
return todoDatabase;
}
void _createDb(Database db, int version) async {
await db.execute('''
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
created_at DATETIME,
updated_at DATETIME
)
''');
}
Future<Database> get database async {
if (_database == null) {
_database = await initDb();
}
return _database;
}
Future<List<Map<String, dynamic>>> select() async {
Database db = await this.database;
var mapList = await db.query('user', orderBy: 'name');
return mapList;
}
//create databases
Future<int> insert(User object) async {
Database db = await this.database;
int count = await db.insert('user', object.toMap());
return count;
}
//update databases
Future<int> update(User object) async {
Database db = await this.database;
int count = await db.update('user', object.toMap(),
where: 'id=?',
whereArgs: [object.id]);
return count;
}
//delete databases
Future<int> delete(int id) async {
Database db = await this.database;
int count = await db.delete('user',
where: 'id=?',
whereArgs: [id]);
return count;
}
Future<List<User>> getUserList() async {
var userMapList = await select();
int count = userMapList.length;
List<User> userList = List<User>();
for (int i=0; i<count; i++) {
userList.add(User.fromMap(userMapList[i]));
}
return userList;
}
}
Below is the user.dart file. It is in the model folder.
class User {
int _id;
String _name;
String _username;
DateTime _created_at;
DateTime _updated_at;
// konstruktor versi 1
User(this._name, this._created_at, this._updated_at);
// konstruktor versi 2: konversi dari Map ke User
User.fromMap(Map<String, dynamic> map) {
this._id = map['id'];
this._name = map['name'];
this._created_at = map['created_at'];
this._updated_at = map['updated_at'];
}
// getter
int get id => _id;
String get name => _name;
DateTime get created_at => _created_at;
DateTime get updated_at => _updated_at;
// setter
set name(String value) {
_name = value;
}
set created_at(DateTime value) {
_created_at = value;
}
set updated_at(DateTime value) {
_updated_at = value;
}
Map<String, dynamic> toMap() {
Map<String, dynamic> map = Map<String, dynamic>();
map['id'] = this._id;
map['name'] = name;
map['created_at'] = created_at;
map['updated_at'] = updated_at;
return map;
}
}
The pubspec.yaml file has the following dependencies.
cupertino_icons: ^0.1.2
flutter_launcher_icons: ^0.7.4
sqflite: any
path_provider: ^1.5.1
The error log I'm getting in the debug console is this:
lib/main.dart:11:55: Error: Too few positional arguments: 1 required, 0 given.
'/': (BuildContext context) => new CreateAccountPage(),
^
lib/pages/create_account/create_account_page.dart:5:7: Context: Found this candidate, but the arguments don't match.
CreateAccountPage(this.user);
^^^^^^^^^^^^^^^^^
lib/main.dart:16:69: Error: Too few positional arguments: 1 required, 0 given.
'/create_account': (BuildContext context) => new CreateAccountPage(),
^
lib/pages/create_account/create_account_page.dart:5:7: Context: Found this candidate, but the arguments don't match.
CreateAccountPage(this.user);
^^^^^^^^^^^^^^^^^
I very much need to be able to pass the right argument inside the CreateAccountPage().
I'm expecting this code to work without any argument errors. Thank you!
The error says you are missing an argument when you are calling CreateAccountPage().
Its constructor is CreateAccountPage(this.user), so you need either provide a User object when you're creating the account page, or remove the parameter from the constructor.
In my application I would like to display vaccines according to the species of the animal (If is a dog or cat). I'm experiencing an error: The method 'map' was called on null. Tried calling: map DropdownMenuItem. Why is this happening? I already put async and await in the methods, I don't understand why it is still null. Bellow my code:
1) This is where I call my DropdownContent class in init to prepare my DropdownMenuItem in the row inside the widget
class _VaccineDetailFormState extends State<VaccineDetailForm> {
final DataRepository repository = DataRepository();
String selectedVaccine = "Select";
List<String> vaccinesBySpecie;
initState() {
DropdownContent.getVaccines(widget.selectedPetID).then((value) => vaccinesBySpecie = value);
}
#override
Widget build(BuildContext context) {
return Scaffold(
[...]
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new DropdownButton<String>(
value: widget.vaccine.name == null ? selectedVaccine: widget.vaccine.name,
underline: Container(
height: 2,
color: Colors.grey,
),
onChanged: (String newValue) {
setState(() {
selectedVaccine = newValue;
widget.vaccine.name = newValue;
});
},
items: vaccinesBySpecie.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
]),
[...]
2) Here is the DropdownContent class that, inside the getVaccines() method, searches the repository to find out the species of the current animal and then returns the appropriate vaccine list.
class Dropdown content
static Future<List<String>> getVaccines(String petId) async {
final DataRepository repository = DataRepository();
String currentSpecie = await repository.getSpecie(petId);
if (currentSpecie.contains('Dog')) {
return listOfVaccinesForDogs();
}
if (currentSpecie.contains('Cat')) {
return listOfVaccinesForCats();
}
}
3) Finally, the repository class that searches for the species of the animal
class Repository
Future<String> getSpecie(String petId) async {
DocumentReference documentReference = petCollection.document(petId);
await documentReference.get().then((snapshot) {
return snapshot.data['specie'].toString();
});
}
While your initState method may be asynchronous, your build method isn't. So at the time that the build method is called, your vaccinesBySpecie method is null.
The best way to fix this would be to initialize your List<String> vaccinesBySpecie like so List<String> vaccinesBySpecie = [];. This way it isn't null when the build method is called.
As a side note, I would suggest using a FutureBuilder or StreamBuilder if you can, that way you can handle when there isn't a value (i.e it is null) vs when there is a value(ie it is not null)
What Dean said illuminated my ideas. I managed to solve it by reaching the following answer:
1) DropdownMenuItem inside the widget
Container(
child: FutureBuilder <List<String>>(
future: DropdownContent.getVaccines(widget.selectedPetID),
builder: (context, AsyncSnapshot snapshot) {
if(snapshot.data == null) {
return CircularProgressIndicator();
}
else {
return DropdownButton<String>(
value: widget.vaccine.name == null? selectedVaccine: widget.vaccine.name,
underline: Container(
height: 2,
color: Colors.grey,
),
onChanged: (String newValue) {
setState(() {
selectedVaccine = newValue;
widget.vaccine.name = newValue;
});
},
items: snapshot.data.map<DropdownMenuItem<String>>((value) =>
new DropdownMenuItem<String>(
child: Text(value),
value: value,
))
.toList(),
);
}
})
)
2) DropdownContent class that searches the repository to find out the species of the current animal and then returns the appropriate vaccine list:
static Future<List<String>> getVaccines(String petId) async {
final DataRepository repository = DataRepository();
String currentSpecie;
await repository.getSpecie(petId).then((value) {
currentSpecie = value;
});
if (currentSpecie.contains('Dog')) {
return listOfVaccinesForDogs();
}
if (currentSpecie.contains('Cat')) {
return listOfVaccinesForCats();
}
}
3) The repository class that searches for the species of the animal
Future<String> getSpecie(String petId) async {
DocumentReference documentReference = petCollection.document(petId);
String specie;
await documentReference.get().then((snapshot) {
specie = snapshot.data['specie'].toString();
});
return specie;
}