Flutter SQFlite populate a dropdown list from the database - flutter

I am trying to read the list part of a dropdown list from my SQFlite database, and can't work out how.
Query from database_helper
Future<List<Map>> getFieldData(String animal, String fieldName)
var dbClient = await db;
return await dbClient.rawQuery('SELECT lbOption FROM jkAssessData Where lbAnimal = \'${animal}\' AND lbField = \'${fieldName}\'');
}
I can get it to work with a static list of values
final List<String> _animals = <String>['CATTLE', 'SHEEP', 'GOAT'];
to populate this dropdown list
FormField<String>(
builder: (FormFieldState<String> state) {
return InputDecorator(
decoration: InputDecoration(
labelText: 'Animal Type',
errorText: state.hasError ? state.errorText : null,
),
isEmpty: _animal == '',
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: _animal,
isDense: true,
onChanged: (String newValue) {
setState(() {
assessHed.asAnimal = newValue;
_animal = newValue;
state.didChange(newValue);
});
},
items: _animals.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
);
},
validator: (val) {
//return val != 'SUMMARY' ? 'DETAIL' : 'Please select Type';
return null;
},
),
It's part of several fields on a Form

Start by using the call in the initState or in a onPressed of a RaisedButton.
Place this initState and _loadAnimals method in your State Widget.
List<String> _animals = <String>[];
#override
initState() {
super.initState();
// when loading your widget for the first time, loads animals from sqflite
_loadAnimals();
}
_loadAnimals() async {
// gets data from sqflite
final loadedAnimals = await getFieldData('CATTLE','Breed1');
setState(() {
// converts sqflite row data to List<String>, updating state
_animals = loadedAnimals.map((Map<dynamic, dynamic> row) => row["lbOption"] as String).toList();
});
}
Or you could change the getFieldData to return a List<String> instead of a List<Map>:
Future<List<String>> getFieldDataAsString(String animal, String fieldName) async {
var dbClient = await db;
var results = await dbClient.rawQuery('SELECT lbOption FROM jkAssessData Where lbAnimal = \'$animal\' AND lbField = \'$fieldName\'');
return results.map((Map<String, dynamic> row) {
return row["lbOption"] as String;
}).toList();
}

Related

Flutter Multi Select throws setState() called after dispose():

I have following 2 drop down in my code
one is drodown and other one is multi select drop down
dropdownButtonFormField(
value: dropdownValueLvl,
decoration: InputDecoration(
prefixIcon: Icon(Icons.leaderboard_outlined),
),
isExpanded: true,
hint: Text('Location'),
onChanged: (String value) {
// This is called when the user selects an item.
// dropdownValueLvl = value;
if (mounted) {
setState(() {
dropdownValueLvl = value;
_selectedRoles;
});
}
},
validator: (String value) {
if (value?.isEmpty ?? true) {
return 'Please select Location ';
}
return null;
},
items: _locddl.map((item) {
return DropdownMenuItem<String>(
value: item.id,
child: Text(item.locationyName),
);
}).toList(),
),
FutureBuilder(
future:
Provider.of<Salesmans>(context, listen: false)
.getUser(),
builder: (BuildContext context,
AsyncSnapshot<Salesman> snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
return MultiSelectField<DdlValues>(
//key: _multiSelectKey,
// initialValue: _listRoles
// .where((element) => _editeSalesman.ddlRoles
// .contains((element).id))
// .toList(),
initialValue: _selectedRoles,
buttonText: 'Select Roles',
title: 'Select Roles',
stream: _listRoles,
onConfirm: (values) {
_selectedRoles = values;
});
} else {
return CircularProgressIndicator();
}
}),
All crud operation are performed properly but If I validate form and select Normal drop down and then select value from multi select drop down, It throws setState() called after dispose():
MultiSelectField.dart as follow
class MultiSelectField<V extends DdlValues> extends StatefulWidget {
final List<V> stream;
final List<V> initialValue;
final FormFieldSetter<List<V>> onConfirm;
final String buttonText;
final String title;
const MultiSelectField({
Key key,
#required this.stream,
#required this.initialValue,
#required this.onConfirm,
#required this.buttonText,
#required this.title,
}) : super(key: key);
#override
_MultiSelectFieldState createState() => _MultiSelectFieldState<V>();
}
class _MultiSelectFieldState<V extends DdlValues>
extends State<MultiSelectField<V>> {
#override
Widget build(BuildContext context) {
// return StreamBuilder(
// stream: widget.stream,
// builder: (BuildContext context, AsyncSnapshot<List<V>> snapshot) {
print(widget.initialValue);
print("Mainwid Key" + widget.key.toString());
//MultiSelectDialogField
return MultiSelectDialogField(
// key: widget.key,
buttonText: Text(widget.buttonText),
title: Text(widget.title),
items: widget.stream
.map((item) => MultiSelectItem(item, item.name))
.toList(),
initialValue: widget.initialValue,
onConfirm: widget.onConfirm,
listType: MultiSelectListType.LIST,
validator: (List value) {
if (value?.isEmpty ?? true) {
return 'Please select Roles ';
}
return null;
},
);
// });
}
}
Earlier in My code, If I validate then following save was called
Future<void> _saveForm() async {
final isValid = _form.currentState.validate();
FocusScope.of(context).unfocus(); // This line was causing issue
bool isSuccess = true;
if (!isValid) {
return;
}
// save code
}
If Validate form and select Dropdown value and then Multi select value was throwing setState() called after dispose():
I changed code as below and now its working
Future<void> _saveForm() async {
final isValid = _form.currentState.validate();
bool isSuccess = true;
if (!isValid) {
return;
}
FocusScope.of(context).unfocus(); // move this line to after if condition
// save code
}

How to make DropdownButtonFormField data from sqflite?

class SQLHelper
Future<List<Map<String, dynamic>>> selectAllServer() async {
final db = await init();
return db.query('server', orderBy: "ip");
}
class ModalDialog
List<Map<String, dynamic>> _server = [];
void getDataDB() async {
final data = await db.selectAllServer();
setState(() {
_server = data;
});
}
DropdownButtonFormField(
onChanged: (val) {},
items: _server.map((ip) {
return DropdownMenuItem(
value: ip['ip'],
child: Text(ip['ket']),
);
}).toList(),
)
Why Value and Child DropdownMenuItem now show?
I want to make Dropdown, value and title get from sqflite.

How do I pass the value selected from DropDown menu to a stateful widget right below it in Flutter/Dart?

I need the user to select a value from a DropDown menu. Based on the value selected, a second DropDown is populated with values from the DB. I tried the following approach and it works when the user selects a value for the first time.
First DropDown:
DropdownButton<String>(
hint: Text('Please select your State/UT'),
style: TextStyle(color: kPrimaryGrey),
focusColor: kPrimaryBlue,
value: selectedState,
items: states.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (val) {
setState(() {
selectedState = val;
});
}),
SizedBox(
height: 25.0,
),
selectedState == null
? SizedBox(
height: 0.0,
)
: CampusSelector(
inputState: selectedState,
),
Second DropDown Using a FutureBuilder (SearchableDropdown):
class CampusSelector extends StatefulWidget {
final String inputState;
CampusSelector({this.inputState});
#override
_CampusSelectorState createState() => _CampusSelectorState();
}
class _CampusSelectorState extends State<CampusSelector> {
var allInstitutions = {};
QuerySnapshot stateData;
List<DropdownMenuItem> listOfDistricts = [];
List<DropdownMenuItem> listOfInstitutions = [];
List<DropdownMenuItem> listOfInsAndDisTmp = [];
List<DropdownMenuItem> listOfInsAndDis = [];
Future<void> getStateData;
List<String> institutions = [];
Future<void> fetchInstitutions(inputState) async {
List<String> institutionsTmp = [];
listOfInstitutions = [];
List<InstitutionAndDistrict> institutionsAndLocation = [];
stateData = await _db
.collection('institutions')
.where('STATE', isEqualTo: inputState)
.getDocuments();
for (int i = 0; i < stateData.documents.length; i++) {
String ins = stateData.documents[i].data['NAME_OF_INSTITUTION'];
String dis = stateData.documents[i].data['DISTRICT'];
institutionsTmp.add(stateData.documents[i].data['NAME_OF_INSTITUTION']);
institutionsAndLocation
.add(InstitutionAndDistrict(institution: ins, district: dis));
}
// Deduplication and Sorting
institutions = institutionsTmp.toSet().toList();
institutions.sort((a, b) => a.toString().compareTo(b.toString()));
debugPrint(institutions.toString());
for (int i = 0; i < institutions.length; i++) {
listOfInstitutions.add(DropdownMenuItem(
child: Text(institutions[i]),
value: institutions[i],
));
}
for (int i = 0; i < institutionsAndLocation.length; i++) {
listOfInsAndDisTmp.add(DropdownMenuItem(
child: ListTile(
title: Text(institutionsAndLocation[i].institution),
subtitle: Text(institutionsAndLocation[i].district),
),
value: institutionsAndLocation[i].institution,
));
}
setState(() {
listOfInsAndDis = listOfInsAndDisTmp;
});
}
#override
void initState() {
getStateData = fetchInstitutions(selectedState);
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: getStateData,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return SearchableDropdown.single(
items: listOfInsAndDis,
value: selectedInstitution,
hint: "Please select your college/university",
searchHint: "Select one",
onChanged: (value) {
setState(() {
selectedInstitution = value;
for (int i = 0; i < stateData.documents.length; i++) {
if (stateData.documents[i].data['NAME_OF_INSTITUTION'] ==
value) {
selectedDistrict =
stateData.documents[i].data['DISTRICT'];
}
}
print(selectedInstitution);
print(selectedDistrict);
});
},
isExpanded: true,
);
} else {
return SizedBox(
height: 0.0,
);
}
});
}
}
CampusSelector class is still a work-in-progress, hence the unruly bunch of lists to process the data.
You populate your CampusBuilder class once in initState. If you want to populate it with different data on each build, you need to call the fetchInstitutions methods in the build method.
That will probably mean you have to use a FutureBuilder.

Getter method returning null value

I have developed a login page using local db in flutter SQL. I want to display the username in SQL database after the table is created but the getter method is returning null.
I am displaying the username like this in class home
body: Center(
child: user != null ? Text("Saved \n \n Username: '${user.username}' ")
: Text("Not saved"),
),
Here is login page code
BuildContext _ctx;
bool _isLoading = false;
final _formKey = new GlobalKey<FormState>();
final scaffoldKey = new GlobalKey<ScaffoldState>();
String _username,_password;
LoginPagePresenter _presenter;
#override
void initState(){
_presenter = new LoginPagePresenter(this);
}
void _submit(){
final form = _formKey.currentState;
if(form.validate()){
_isLoading = true;
form.save();
_presenter.doLogin(_username, _password);
}
}
void _showsnackbar(String text){
scaffoldKey.currentState.showSnackBar(new SnackBar(
content: new Text(text),
));
}
#override
Widget build(BuildContext context) {
return _isLoading ? Loading() :Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
title: Text('Login Page',textAlign: TextAlign.center,),
),
body: Container(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 50.0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
SizedBox(height: 20.0),
TextFormField(
decoration: InputDecoration(labelText:'Username' ),
validator: (val) => val.isEmpty ? 'Enter Username' : null,
onChanged: (val) {
setState(() => _username = val);
},
),
SizedBox(height: 20.0),
TextFormField(
obscureText: true,
decoration: InputDecoration(labelText:'Password' ),
validator: (val) => val.length < 6 ? 'Enter a password 6+ chars long' : null,
onChanged: (val) {
setState(() => _password = val);
},
),
SizedBox(height: 20.0),
RaisedButton(
color: Colors.pink[400],
child: Text(
'Sign In',
style: TextStyle(color: Colors.white),
),
onPressed: () async {
_submit();
}
),
],
),
),
),
);
}
#override
void onLoginError(String error) {
_showsnackbar(error);
setState(() {
_isLoading = false;
});
}
#override
void onLoginSuccess(User user) async {
_showsnackbar(user.toString());
setState(() {
_isLoading = false;
});
var db = new DatabaseHelper();
await db.saveUser(user);
Navigator.of(_ctx).push(MaterialPageRoute<Null>(
builder: (BuildContext context){
return new Home(
user:user,
);
}
));
}
Here is user class
class User{
String _username;
String _password;
User(this._username,this._password);
User.map(dynamic obj){
this._username = obj['username'];
this._password = obj['password'];
}
String get username => _username;
String get password => _password;
Map<String,dynamic> toMap(){
var map = new Map<String,dynamic>();
map["username"] = _username;
map["password"] = _password;
return map;
}
}
And this is database helper class
class DatabaseHelper{
static final DatabaseHelper _instance = new DatabaseHelper.internal();
DatabaseHelper.internal();
factory DatabaseHelper() => _instance;
static Database _db;
Future<Database> get db async{
if(_db!= null)
{
return _db;
}
_db = await initdb();
return _db;
}
initdb() async{
Directory documentDirectory = await getApplicationDocumentsDirectory();
String path = join(documentDirectory.path,"main.db");
var ourDb = await openDatabase(path,version:1,onCreate:_onCreate);
return ourDb;
}
void _onCreate(Database db,int version)async {
await db.execute("CREATE TABLE User(id INTEGER PRIMARY KEY,username TEXT,password TEXT)");
print("Table created");
}
//insertion of data
Future<int> saveUser(User user)async {
var dbClient = await db;
int res = await dbClient.insert("User", user.toMap());
return res;
}
// deleting data
Future<int> deleteUser(User user)async {
var dbClient = await db;
int res = await dbClient.delete("User");
return res;
}
}
this is login presenter
abstract class LoginPageContract{
void onLoginSuccess(User user);
void onLoginError(String error);
}
class LoginPagePresenter{
LoginPageContract _view;
RestData api = new RestData();
LoginPagePresenter(this._view);
doLogin(String username,String password){
api
.login(username, password)
.then((user)=> _view.onLoginSuccess(user))
.catchError((onError)=>_view.onLoginError(onError()));
}
}
This is github link to the code for reference: https://github.com/meadows12/sql
Please help !!
All you have to do is to access the user obejct as "widget.user" instead of "user". So, the following would do the trick :
body: Center(
child: widget.user != null ? Text("Saved \n \n Username: '${widget.user.username}' ")
: Text("Not saved"),
)
There's one more problem in the code. You are not assigning the buildcontext to variable _ctx. So, the screen change didn't happen on your code on the github. I added one line in login.dart as below to make it work :
Widget build(BuildContext context) {
_ctx = context;
return _isLoading ? Loading() :Scaffold(______________
Result :

Flutter How to populate data from sqflite to dropdown list

I have seen the questions in stackoverflow which are quite similar to my question, but those question and answer dint work for me. So here is my question how to populate data from sqflite to dropdown list. Below are the dart files which I have written.Please help me with the question
dbhelper.dart
import 'package:abc/model/manage_categories.dart';
import 'package:sqflite/sqflite.dart';
import 'dart:async';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
class DatabaseHelper {
static DatabaseHelper _databaseHelper; // Singleton DatabaseHelper
static Database _database; // Singleton Database
String categoriesTable = 'categories_table';
String colId = 'id';
String colTitle = 'title';
String colDate = 'date';
DatabaseHelper._createInstance(); // Named constructor to create instance of DatabaseHelper
factory DatabaseHelper() {
if (_databaseHelper == null) {
_databaseHelper = DatabaseHelper._createInstance(); // This is executed only once, singleton object
}
return _databaseHelper;
}
Future<Database> get database async {
if (_database == null) {
_database = await initializeDatabase();
}
return _database;
}
Future<Database> initializeDatabase() async {
// Get the directory path for both Android and iOS to categories database.
Directory directory = await getApplicationDocumentsDirectory();
String path = directory.path + 'categoriess.db';
// Open/create the database at a given path
var categoriessDatabase = await openDatabase(path, version: 1, onCreate: _createDb);
return categoriessDatabase;
}
void _createDb(Database db, int newVersion) async {
await db.execute('CREATE TABLE $categoriesTable($colId INTEGER PRIMARY KEY AUTOINCREMENT, $colTitle TEXT, '
'$colDate TEXT)');
}
// Fetch Operation: Get all categories objects from database
Future<List<Map<String, dynamic>>> getCategoriesMapList() async {
Database db = await this.database;
// var result = await db.rawQuery('SELECT * FROM $categoriesTable order by $colTitle ASC');
var result = await db.query(categoriesTable, orderBy: '$colTitle ASC');
return result;
}
// Insert Operation: Insert a categories object to database
Future<int> insertCategories(Categories categories) async {
Database db = await this.database;
var result = await db.insert(categoriesTable, categories.toMap());
return result;
}
// Update Operation: Update a categories object and save it to database
Future<int> updateCategories(Categories categories) async {
var db = await this.database;
var result = await db.update(categoriesTable, categories.toMap(), where: '$colId = ?', whereArgs: [categories.id]);
return result;
}
Future<int> updateCategoriesCompleted(Categories categories) async {
var db = await this.database;
var result = await db.update(categoriesTable, categories.toMap(), where: '$colId = ?', whereArgs: [categories.id]);
return result;
}
// Delete Operation: Delete a categories object from database
Future<int> deleteCategories(int id) async {
var db = await this.database;
int result = await db.rawDelete('DELETE FROM $categoriesTable WHERE $colId = $id');
return result;
}
// Get number of categories objects in database
Future<int> getCount() async {
Database db = await this.database;
List<Map<String, dynamic>> x = await db.rawQuery('SELECT COUNT (*) from $categoriesTable');
int result = Sqflite.firstIntValue(x);
return result;
}
// Get the 'Map List' [ List<Map> ] and convert it to 'categories List' [ List<Categories> ]
Future<List<Categories>> getCategoriesList() async {
var categoriesMapList = await getCategoriesMapList(); // Get 'Map List' from database
int count = categoriesMapList.length; // Count the number of map entries in db table
List<Categories> categoriesList = List<Categories>();
// For loop to create a 'categories List' from a 'Map List'
for (int i = 0; i < count; i++) {
categoriesList.add(Categories.fromMapObject(categoriesMapList[i]));
}
return categoriesList;
}
}
Add_store_item.dart
import 'package:flutter/material.dart';
import 'package:abc/database/dbhelper_categories.dart';
import 'package:abc/database/dbhelper_manage_inventory.dart';
import 'package:abc/model/Manageinventory_class.dart';
import 'package:abc/model/manage_categories.dart';
class AddStoreItem extends StatefulWidget {
final Inventory inventory;
AddStoreItem(this.inventory);
#override
State<StatefulWidget> createState() => new AddStoreItemState();
}
class AddStoreItemState extends State<AddStoreItem> {
DatabaseHelper databaseHelper = DatabaseHelper();
List<Categories> categoriesList = <Categories>[];
int count = 0;
DBProvider _db = DBProvider();
TextEditingController _itemController;
TextEditingController _quantityController;
TextEditingController _categoryController;
TextEditingController _unitController;
TextEditingController _locationController;
#override
void initState() {
super.initState();
_loadCategorieslist();
_itemController = new TextEditingController(text: widget.inventory.item);
_quantityController = new TextEditingController(text: widget.inventory.quantity);
_categoryController = new TextEditingController(text: widget.inventory.category);
_unitController = new TextEditingController(text: widget.inventory.unit);
_locationController = new TextEditingController(text: widget.inventory.location);
}
_loadCategorieslist()async{
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add Inventory')
),
body: SingleChildScrollView(
child: Container(
margin: EdgeInsets.all(15.0),
alignment: Alignment.center,
child: Column(
children: <Widget>[
SizedBox(height: 10),
TextField(
controller: _itemController,
decoration: InputDecoration(labelText: 'Item'),
),
SizedBox(height: 10),
TextField(
controller: _quantityController,
decoration: InputDecoration(labelText: 'Quantity'),
),
SizedBox(height: 10),
DropdownButton<String>(
value: categoriesList,
items: categoriesList.map((String){
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}),
isExpanded: true,
onChanged: (value) {
print("value: $value");
},
hint: Text(
"Units",
style: TextStyle(
// color: Colors.black,
),
),
),
DropdownButton<String>(
items: [
DropdownMenuItem<String>(
value: "1",
child: Text(
"First",
),
),
DropdownMenuItem<String>(
value: "2",
child: Text(
"Second",
),
),
],
isExpanded: true,
onChanged: (value) {
print("value: $value");
},
hint: Text(
"Location",
style: TextStyle(
// color: Colors.black,
),
),
),
SizedBox(height: 10),
RaisedButton(
child: (widget.inventory.id != null) ? Text('Update') : Text('Add Inventory'),
onPressed: () {
_addInventory();
},
),
],
),
),
),
);
}
void _addInventory() {
if (widget.inventory.id != null) {
_db
.updateNote(Inventory.withId(
widget.inventory.id,
_itemController.text,
_quantityController.text,
_categoryController.text,
_unitController.text,
_locationController.text,
))
.then((_) => Navigator.pop(context, 'update'));
} else {
_db
.saveNote(Inventory(
_itemController.text,
_quantityController.text,
_categoryController.text,
_unitController.text,
_locationController.text,
))
.then((_) => Navigator.pop(context, 'save'));
}
}
}
This how I managed to populate the list of data from sqflite to drop down
Declared list from sqflite to AddStoreItemState as below
DatabaseHelper databaseHelper = DatabaseHelper();
List<Categories> categoriesList ;
Categories _category;
Now wrap the drop down button as below
Center(
child: DropdownButtonFormField<Categories>(
hint: Text('Categories'),
value: _category,
onChanged: (Categories value){
setState(() {
_category = value;
});
},
items: categoriesList.map((user) => DropdownMenuItem<Categories>(
child: Text(user.cname),
value: user,
)
).toList(),
),
),
In AddStoreItemState change your _loadCategorieslist()async method to:
Future<List<Categories>> _loadCategorieslist() async => databaseHelper.getCategoriesMapList();
And wrap your DropdownButton with a FutureBuilder
This is how I read data from SqfLite to use it in a drop-down or data table, in this case barcode records.
Here is the model, which includes a fromJson() function like below:
class Barcode {
String code;
String itemNo;
Barcode({
this.code,
this.itemNo,
});
Map<String, dynamic> toMap() => {
'code': code,
'itemNo': itemNo,
};
factory Barcode.fromJson(Map<String, dynamic> parsedJson) {
return Barcode(
code: parsedJson['code'],
itemNo: parsedJson['itemNo'],
);
}
}
Here is how I read barcodes (all) from SqfLite:
static Future<List<Barcode>> getAll() async {
final db = await DbUtil.database;
var response = await db.query(tableName);
List<Barcode> list = response.map((c) => Barcode.fromJson(c)).toList();
return list;
}
Here is for reading just one barcode:
static Future<Barcode> get(String barcode) async {
final db = await DbUtil.database;
var response = await db.query(tableName, where: "$pkName = ?", whereArgs: ['$barcode']);
return response.isNotEmpty ? Barcode.fromJson(response.first) : null;
}
Then to call it:
var barcode = await BarcodeDb.get(scanText);
Try this out, it should work for you.