"There should be exactly one item with [DropdownButton]'s value: 1. \nEither zero or 2 or more [DropdownMenuItem]s were detected with the same value" - flutter-web

I have this code:
import 'dart:convert';
import 'package:hr_jobform/customColors.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class CitiesStateDropdownButton extends StatefulWidget {
const CitiesStateDropdownButton({super.key});
#override
State<CitiesStateDropdownButton> createState() =>
CitiesStateDropdownButtonState();
}
class CitiesStateDropdownButtonState
extends State<CitiesStateDropdownButton> {
#override
void initState() {
_getStatesList();
super.initState();
}
#override
Widget build(BuildContext context) {
return Wrap(children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(8, 15, 8, 8),
child: ConstrainedBox(
constraints: BoxConstraints.tight(const Size(219, 50)),
child: DropdownButton<String>(
value: _state,
iconSize: 30,
icon: (null),
style: TextStyle(
color: Palette.customColors,
fontSize: 16,
),
hint: Text('Select State'),
onChanged: (String? newValue) {
setState(() {
_state = newValue!;
_getCitiesList();
print(_state);
});
},
items: states?.map((item) {
return new DropdownMenuItem(
child: new Text(item['name']),
value: item['id'].toString(),
);
})?.toList() ??
[],
),
),
),
//======================================================== City
Wrap(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(8, 15, 8, 8),
child: ConstrainedBox(
constraints: BoxConstraints.tight(const Size(187, 50)),
child: DropdownButton<String>(
value: _city,
iconSize: 30,
icon: (null),
style: TextStyle(
color: Palette.customColors,
fontSize: 16,
),
hint: Text('Select City'),
onChanged: (String? newValue) {
setState(() {
_city = newValue!;
print(_city);
});
},
items: cities?.map((item) {
return new DropdownMenuItem(
child: new Text(item['name']),
value: item['id'].toString(),
);
})?.toList() ??
[],
),
),
),
],
),
]);
}
//=============================================================================== Api Calling here
//CALLING State API HERE
// Get Departamento information by API
late List states = [];
late String _state = '1';
String statesUrl =
"http://localhost:3000/states/getAllStates";
_getStatesList() async {
try {
http.Response response = await http.get(Uri.parse(statesUrl));
if (response.statusCode == 200) {
print(response.body);
var data = jsonDecode(response.body);
setState(() {
states = data['state'];
});
return jsonDecode(response.body);
} else {
return ("Server Error");
}
} catch (e) {
return print(e);
}
}
// Get Cities information by API
late List cities = [];
late String _city = '1';
_getCitiesList() async {
try {
http.Response response = await http.get(Uri.parse("http://localhost:3000/cities/getAllCities?id=$_state"));
if (response.statusCode == 200) {
print(response.body);
var data = jsonDecode(response.body);
setState(() {
cities = data['City'];
print(cities);
});
return jsonDecode(response.body);
} else {
return ("Server Error");
}
} catch (e) {
return print(e);
}
}
}
It runs well, but when i select one of the States on my dropdown list, the cities api call is successful as it appears on console, but the cities dropdown gives me this error:
Assertion failed:
items == null || items.isEmpty || value == null ||
items.where((DropdownMenuItem<T> item) {
return item.value == value;
}).length == 1
"There should be exactly one item with [DropdownButton]'s value: 1. \nEither zero or 2 or more [DropdownMenuItem]s were detected with the same value"
Please I've been struggling with this error for a couple of days now, I would appreciate if you can help me.

While using with future items from dropDown, use nullable value for DropdownButton.
String? _state;
String? _city;
And set Value on successful response only, otherwise set _city=null and _state = null
setState(() {
final List<String> items = data['City'].toList().toSet(); // trying to get unique value set
cities = items.toList();
_city = cities.first;
print(cities);
});
Do the same for others

Related

How to handle API subqueries in Flutter more faster?

It's me again and suffering with async and await functions of Flutter! Since i begin with this i think i hate the Future 😒😵‍💫
Well, I'm developing an API REST with NodeJS and Sequelize, i was good until today. I have a cloud database with tables that contains a lot of relations with other tables, so these tables has different id fields. In this case, i'm working with materials_requisitions table, this table has various tables related. I brought all of the data of this table to my flutter app and drew it in one Screen. All OK but the user only can see id numbers on the widgets! So i made different async's getter functions to take the id of each field, search it in its corresponding table and return the information about that field completely. All is working well but it takes a lot of time!!!!
I have a list of materials_requisitions and i have to map field to field to show user more readable data.
Do you have any idea how to accelerate or do this subquerys more faster?? Or another way to do it better??
This is my solicitud_materiales.dart file, here i charge and draw the data:
import 'dart:convert';
import 'package:app_vivero/details/solicitud_materiales_detailN.dart';
import 'package:app_vivero/functions/api/requestHeadersToken.dart';
import 'package:app_vivero/functions/emptyBody.dart';
import 'package:app_vivero/functions/globals.dart';
import 'package:app_vivero/lists/GangsGetters.dart';import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../functions/globals.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import '../functions/navigationDrawer.dart';
import '../lists/DepartmentsGetters.dart';
import '../lists/EmployeesGetters.dart';
import '../lists/Materials_RequisitionsGetters.dart';
//lista de tipo de ejecuciones
const List<String> solicitud_tipo = <String>[
'MATERIAL',
'EPP',
'DOTACION',
];
class Solicitud_MaterialesPage extends StatefulWidget {
const Solicitud_MaterialesPage({Key? key}) : super(key: key);
#override
State<Solicitud_MaterialesPage> createState() => _Solicitud_MaterialesPageState();
}
class _Solicitud_MaterialesPageState extends State<Solicitud_MaterialesPage> {
String dropdownValue = solicitud_tipo.first;
int _paginaActual=0;
String _page_title = "Jornal";
List smList =[];
List filteredList = [];
//MAPEO DATOS LIST SM
String department_name='';
String gang_name='';
String type_name='';
String priority_name='';
String employee_name='';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Solicitud de Materiales'),
),
drawer: const navigationDrawerPersonalized(),
body:Column(
children:[
Container(
decoration: const BoxDecoration(
color: Colors.white,
),
child: DropdownButtonFormField<String>(
decoration: const InputDecoration(
icon: Padding(padding: EdgeInsets.only(left: 10.0),
child: Icon(FontAwesomeIcons.cartShopping),
),
labelText: "Tipo",
),
hint: const Text("Seleccioná un tipo de solicitud"),
value: dropdownValue,
elevation: 16,
onChanged: (String? value) {
// This is called when the user selects an item.
setState(() {
dropdownValue = value!;
});
},
items: solicitud_tipo.map<DropdownMenuItem<String>>((
String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
_body(),]),
);
}
Widget _body() {
switch (dropdownValue) {
case 'MATERIAL':
return MaterialSMBody('type_id',dropdownValue);
case 'EPP':
return MaterialSMBody('type_id',dropdownValue);
case 'DOTACION':
return MaterialSMBody('type_id',dropdownValue);
default:
return MaterialSMBody('type_id',dropdownValue);
}//SWITCH
}
DataRow _getDataRow(index, data) {
return DataRow(
cells: <DataCell>[
DataCell(Text(data['sm_id'].toString(),style: const TextStyle(color: Colors.lightBlue,decoration: TextDecoration.underline),) , onTap: (){
Navigator.push(context,MaterialPageRoute(builder: (context) => Solcitud_Materiales_Details(data['sm_id'])));
}),
DataCell(Text(data['date_record'])),
DataCell(Text(data['type_id'].toString())),
DataCell(Text(data['priority_id'].toString())),
DataCell(Text(data['department_id'])),
DataCell(Text(data['gang_id'].toString())),
DataCell(Text(data['user_id'].toString())),
],
);
}
//Traer planes con el metodo GET
Future getSM() async {
final prefs = await SharedPreferences.getInstance();
var employeeId = prefs.getInt('employeeId');
var company = prefs.getInt('userCompanyId');
var result = await http.get(Uri.parse('$apiLink/materials_requisitions/list/$company/$employeeId'),headers: getTokenWithContentType(prefs));
if (result.statusCode == 404){
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
actions: [
TextButton(
child: const Text('Aceptar'),
onPressed: () {
Navigator.of(context).pop();
})
],
icon: const Icon(Icons.warning,
color: Colors.yellow, size: 50.0),
title: const Text('WARNING!'),
contentPadding: const EdgeInsets.fromLTRB(
40.0, 10.0, 40.0, 10.0),
content: Text(
result.body));
});
}else if (result.statusCode == 200) {
List ModifiedSM = json.decode(result.body);
for (var i = 0; i < ModifiedSM.length; i++) {
ModifiedSM[i]['department_id'] = await getListDepartment(ModifiedSM[i]['department_id']);
ModifiedSM[i]['type_id'] = await getListType(ModifiedSM[i]['type_id']);
ModifiedSM[i]['priority_id'] = await getListPriority(ModifiedSM[i]['priority_id']);
ModifiedSM[i]['gang_id'] = await getListGang(ModifiedSM[i]['gang_id']);
ModifiedSM[i]['user_id'] = await getListEmployee(ModifiedSM[i]['user_id']);
}
return ModifiedSM;
} else {
throw Exception('Failed');
}
}
//TRAER DATOS DE DEPARTAMENTO PARA REEMPLAZARLOS POR ID EN LA LISTA
getListDepartment(department_id) async {
var department= await getDepartment(department_id);
return department[0]['name'];
}
//TRAER DATOS DE CUADRILLA PARA REEMPLAZARLOS POR ID EN LA LISTA
getListGang(gang_id) async {
var gang= await getGang(gang_id);
return gang[0]['name'];
}
//TRAER DATOS DE TIPO DE SM PARA REEMPLAZARLOS POR ID EN LA LISTA
getListType(type_id) async {
var type= await getType(type_id);
return type[0]['name'];
}
//TRAER DATOS DE PRIORIDAD DE SM PARA REEMPLAZARLOS POR ID EN LA LISTA
getListPriority(priority_id) async {
var priority= await getPriority(priority_id);
return priority[0]['name'];
}
//TRAER DATOS DE EMPLEADO PARA REEMPLAZARLOS POR ID EN LA LISTA
getListEmployee(employee_id) async {
var employee= await getEmployee(employee_id);
return employee[0]['first_name'] + " " + employee[0]['last_name'];
}
Widget MaterialSMBody(String filter_column, String filter_value){
return GestureDetector(
child: FutureBuilder(
future: getSM(),
builder: (context, snapshot) {
if (snapshot.hasData) {
smList = snapshot.data;
final filteredSM = smList.where((element) =>
(element[filter_column] != null
? element[filter_column] == (filter_value)
: false));
filteredList = filteredSM.toList();
return Column(children: [
SingleChildScrollView(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
DataTable(
// Datatable widget that have the property columns and rows.
columns: [
// Set the name of the column
const DataColumn(label: Text('SM #')),
const DataColumn(label: Text('FECHA')),
const DataColumn(label: Text('TIPO')),
DataColumn(
label: DropdownButton(
hint: const Text(
'PRIORIDAD',
style: TextStyle(color: Colors.black),
),
underline: Container(),
items: const [
DropdownMenuItem(
value: 'Urgente',
child: Text('Urgente'),
),
DropdownMenuItem(
value: 'Requerida',
child: Text('Requerida'),
),
],
onChanged: (value) {
//Here update your column values
setState(() {});
},
),
),
const DataColumn(label: Text('DEPARTAMENTO')),
const DataColumn(label: Text('CUADRILLA')),
const DataColumn(label: Text('SOLICITANTE')),
// const DataColumn(label: Text('APROBAR')),
],
rows: List.generate(
filteredList.length,
(index) => _getDataRow(
index,
filteredList[index],
),
),
),
],
),
),
),
],
);
} else {
return emptyBody();
}
}
),
);
}
}
Here is an example of the functions that i use to bring the departments, these are simple api requests:
//Traer datos de departamento en base al id brindado con el metodo http GET
Future getDepartment(int department_id) async {
final prefs = await SharedPreferences.getInstance();
var result = await http.get(Uri.parse('$apiLink/departments/$department_id'),headers: getTokenWithContentType(prefs));
if (result.statusCode == 200) {
return json.decode(result.body);
} else {
throw Exception(result.body);
}
}
Some screenshoots:
My SM screen
It takes so much time!!

Display JSON data in Flutter

I want to display some data I am receiving from an API, this is the data I am receiving from my Node JS server:
[
{
"NAME": "Matematicas"
},
{
"NAME": "Naturales"
},
{
"NAME": "Ciencias Sociales"
},
{
"NAME": "Lenguaje"
},
{
"NAME": "Religion"
}
]
This is how I am receiving the data in my front end (UPDATED WITH #Mofidul Islam):
Future<List<Subject>> fetchSubject() async {
var url = Uri.http('localhost:8000', "/subjects");
var prefs = await SharedPreferences.getInstance();
var token = prefs.getString('token');
final response = await http.get(
Uri.parse('http://localhost:8000/subjects'),
headers: {'x-access-token': token!});
print(response.body);
List<dynamic> list = "[" + response.body + "]" as List<dynamic>;
List<Subject> subjectList = [];
list.forEach((element) {
subjectList.add(Subject.fromJson(element));
});
return subjectList;
}
This is the class to handle the incoming data (UPDATED WITH #Mofidul Islam):
class Subject {
final String name;
Subject({
required this.name,
});
factory Subject.fromJson(Map<String, dynamic> json) {
return Subject(name: json['NAME'] as String);
}
parseJson(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Subject>((json) => Subject.fromJson(json)).toList();
}
}
This is my init state:
void initState() {
super.initState();
futureSubject = fetchSubject();
}
This is how I am trying to display the data:
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Materias',
theme: ThemeData(
primarySwatch: Colors.green,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Materias'),
),
body: Center(
child: FutureBuilder<List<Subject>>(
future: futureSubject,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: snapshot.data!
.map((subject) => Text(subject.name))
.toList(),
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
),
),
);
}
However, the only thing being displayed when the page loads is:
Matematicas
I am trying to achieve 2 things:
Display the data in a list fashion like:
Subject Name
Matematicas
Naturales
Ciencias Sociales
Lenguaje Religion
Be able to use them as a link to another page when clicked
PD: If I remove the index access [0] on return Subject.fromJson(jsonDecode(response.body)[0]);
I get Expected a value of type Map<String, dynamic> but got one of type List<dynamic>
Any recommendations or guide on how to go through this?
Thank you for your time
You need to loop it
parseJson(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed
.map<Subject>((json) => Subject.fromJson(json))
.toList();
}
and change your api call to this
Future<List<Subject>> fetchSubject() async {
.....
return parseJson(json.encode(response.body));
}
sorry for my english.
Please try this one and return List from api method
Future<List<Subject>> fetchSubject() async {
var url = Uri.http('localhost:8000', "/subjects");
var prefs = await SharedPreferences.getInstance();
var token = prefs.getString('token');
final response = await http.get(Uri.parse('http://localhost:8000/subjects'),
headers: {'x-access-token': token!});
List<dynamic>list=response.body as List<dynamic>;
List<Subject>subjectList=[];
list.forEach((element){
subjectList.add(Subject.fromJson(element));
});
return subjectList;
}
refactor UI like this
Center(
child: FutureBuilder<List<Subject>>(
future: futureSubject,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(children: snapshot.data.map((subject)=>Text(subject.name)).toList(),);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
)

How to update the values using NodeJS API

I created a user profile screen, where user can also edit the fields by clicking on edit button, it will appear a popup dialogue where i display the fields which are allowed to edit. And I am using API which i create it using NodeJS to update that fields, it is working, values are updating.
Problem:
There are 5 fields in edit dialogue box (email, phoneNo, address, education and martialstatus), but if user only want to edit the education field, and want rest field remain same, and when user click on update change button where i call the api, it only update the that field which is edited and update the other field to null, because it gets null value from the field controller (i am passing the textfield controller to api body parameter).
Solution which i think:
It can be done using conditions (if-else) to handle the null value, by passing the old values which i am getting using shared preference, but it will be not efficient, because there are 5 fields yet, but if in future if edit fields number increase, then more condition (checks) will be needed.
here is backend api code:
updateprofile = (req,res)=>{
jwt.verify(req.body.token, 'secret' , function(err, decoded) {
if(err) {
err["expiredAt"] = err["expiredAt"].toLocaleString();
res.status(300).json(err)
}
else
{
User.find({"username":req.body.username,_id: req.body.id},function(err,data){
console.log(req.body)
User.updateOne({"username":req.body.username, _id: req.body.id}, {"email":req.body.email,"education":req.body.education,"PhoneNo":req.body.PhoneNo,"address":req.body.address,"martialstatus":req.body.martialstatus},function(err,dat){
if(err)
res.json("You Last login Yesterday");
else
{
var token = jwt.sign({
data: 'foobar'
}, 'secret', { expiresIn: "30 minute"})
res.status(200).json({auth: true, AccessToken: token,email:req.body.email,education:req.body.education,phone:req.body.PhoneNo,address:req.body.address,martialstatus:req.body.martialstatus})
}
})})
}})}
here is edit dialogue and shared preference code:
String getname="";
String getemail="";
String getdesignation="";
String getType="";
String getId="";
String getToken="";
String getEducation="";
String getPhone="";
String getMartialStatus="";
String getAddress="";
String getDob="";
_userDetails() async{
SharedPreferences myPrefs=await SharedPreferences.getInstance();
setState(() {
getname=myPrefs.getString('name');
getemail=myPrefs.getString('email');
getdesignation=myPrefs.getString('designation');
getType=myPrefs.getString('type');
getId=myPrefs.getString('UserId');
getToken=myPrefs.getString('accesstoken');
getPhone=myPrefs.getString('phone');
getEducation=myPrefs.getString('education');
getMartialStatus=myPrefs.getString('martialstatus');
getAddress=myPrefs.get('address');
getDob=myPrefs.get('DOB');
});
}
_displayTextInputDialog(BuildContext context) {
return showDialog(
context: context,
builder: (context) {
return
Center(child:
Column(children: <Widget>[
AlertDialog(
title: Text('Profile Edit'),
content: Column(children: <Widget>[
TextField(
onChanged: (value) {
setState(() {
valueText = value;
});
},
controller: email,
decoration: InputDecoration(hintText: getemail,icon: Icon(Icons.email)),
),
TextField(
onChanged: (value) {
setState(() {
valueText = value;
});
},
controller: phone,
decoration: InputDecoration(hintText: getPhone,icon: Icon(Icons.phone)),
),
TextField(
onChanged: (value) {
setState(() {
valueText = value;
});
},
controller: address,
decoration: InputDecoration(hintText: getAddress,icon: Icon(Icons.location_on)),
),
TextField(
onChanged: (value) {
setState(() {
valueText = value;
});
},
controller: education,
decoration: InputDecoration(hintText: getEducation,icon: Icon(Icons.library_books)),
),
TextField(
onChanged: (value) {
setState(() {
valueText = value;
});
},
controller: martialstatus,
decoration: InputDecoration(hintText: getMartialStatus,icon: Icon(Icons.group)
),
)],),
actions: <Widget>[
FlatButton(
color: Colors.green,
textColor: Colors.white,
child: Text('Update Changes'),
onPressed: () {
setState(() {
valuebutton = valueText;
updateProfile(); //here calling the api function to udpate the values
Navigator.pop(context);
});
},
),
],)
],
));
});
}
here i'm using api and calling the function on update changes button
Dio dio = new Dio();
var data={};
Future updateProfile() async {
try{
var data={
"username":getname,
"id":getId,
"token":getToken,
"email":email.text,
"PhoneNo":phone.text,
"martialstatus":martialstatus.text,
"address":address.text,
"education":education.text
}; dio
.post(localHostUrlUpdateProfile, data: json.encode(data))
.then((onResponse) async {
showDialog(context: context,
builder: (BuildContext context){
return AdvanceCustomAlert(
title: "Profile Updated",
descriptions:"Profile is updated successfully!",icon: Icons.check,
bgcolor: Colors.green,
fgcolor: Colors.green,);
}
);
});}
catch (e) {
print("error");
bool checkStatus =
OSError.noErrorCode == 113 || OSError.noErrorCode == 101;
print(checkStatus);
print(OSError.noErrorCode == 113);
if (e is DioError) {
print(e.response);
}
if (checkStatus == false) {
print("con er");
showDialog(
context: context,
builder: (BuildContext context) {
return AdvanceCustomAlert(
title: "Connection Error",
descriptions: "An unknown network error occured.",
icon: Icons.error,
bgcolor: Colors.red,
fgcolor: Colors.red,
);
});
}
}
}
hope all clear, please help if anyone know how to do this.
i just reduced the condition logic to this
if(email.text==""){
email.text=getemail;
}
if(phone.text==""){
phone.text=getPhone;
}
education.text=getEducation;
}
if(address.text==""){
address.text=getAddress;
}
if(martialstatus.text==""){
martialstatus.text=getMartialStatus;
}
it is working, would appreciate if someone know the better solution than this.

LiveQuery does not work, if there is ParseConnectivityProvider provided

I'm working on a Flutter app, that consists of a employee scheduler with a Parse Server background.The app works as the following:
1)User opens the app;
2)The app shows a list of cards of available employees (if there is some in the server database)
3)The user is able to edit, confirm or delete the employee availability.
The app is working. It means that I'm able to CRUD data also from my Parse Server as from my app screen.
The goal: Every data change in the database needs to refresh (realtime) the user screen, removing, deleting or updating the (changed) data itself.
My idea: use parse_server_sdk: ^2.0.1 as a dependency (pubspec.yaml) and implement Live Queries (as the github docs here)
The Problem: after compilation, despite my AndroidStudio don't crashes visually speaking, nor my application itself, it shows me (logs) that "LiveQuery does not work, if there is ParseConnectivityProvider provided".
Without problems(main.dart):
void main() async{
runApp(MyApp());
}
Causing problems:
void main() async{
//the following parameters are in other file (constants.dart)...they are working
Parse().initialize(
kParseApplicationId,
kParseServerUrl,
masterKey: kParseMasterKey,
clientKey: kParseClientKey,
debug: true,
liveQueryUrl: kLiveQueryUrl,
autoSendSessionId: true,
);
var dietPlan = ParseObject('DietPlan')
..set('Name', 'Ketogenic')
..set('Fat', 65);
await dietPlan.save();
var response = await dietPlan.save();
if (response.success) {
dietPlan = response.result;
}
//until here, the program works well.The data is inserted and retrieved, ok...but exactly here..the problem!
final LiveQuery liveQuery = LiveQuery(); //from here..this is causing problems
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('DietPlan'))
..whereEqualTo('intNumber', 1);
Subscription subscription = await liveQuery.client.subscribe(query);
subscription.on(LiveQueryEvent.delete, (value) {
print('*** DELETE ***: ${DateTime.now().toString()}\n $value ');
print((value as ParseObject).objectId);
print((value as ParseObject).updatedAt);
print((value as ParseObject).createdAt);
print((value as ParseObject).get('objectId'));
print((value as ParseObject).get('updatedAt'));
print((value as ParseObject).get('createdAt'));
});
runApp(MyApp());
}
I didn't found any solution googleing.Any at all. After many research, I've got (going deep in the libraries) that Parse.Initialize(...) have connectivityProvider as one of it's parameters, but I didn't fill that parameter.
Also, the problems starts exactly when I've inserted the the line:
final LiveQuery liveQuery = LiveQuery();
Could someone help me to solve this issue?Thanks in advance
In the following, I'm showing all my code(main.dart):
// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:intl/intl.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:marca_horario/model/data.dart';
import 'package:marca_horario/network_utils/data_utils.dart';
import 'package:http/http.dart';
import 'package:parse_server_sdk/parse_server_sdk.dart';
import 'package:marca_horario/constants.dart';
void myLQ() async {
Parse().initialize(
kParseApplicationId,
kParseServerUrl,
masterKey: kParseMasterKey,
clientKey: kParseClientKey,
debug: true,
liveQueryUrl: kLiveQueryUrl,
autoSendSessionId: true,
);
var dietPlan = ParseObject('DietPlan')
..set('Name', 'Ketogenic')
..set('Fat', 65);
await dietPlan.save();
var response = await dietPlan.save();
if (response.success) {
dietPlan = response.result;
}
final LiveQuery liveQuery = LiveQuery();
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('DietPlan'))
..whereEqualTo('intNumber', 1);
Subscription subscription = await liveQuery.client.subscribe(query);
subscription.on(LiveQueryEvent.delete, (value) {
print('*** DELETE ***: ${DateTime.now().toString()}\n $value ');
print((value as ParseObject).objectId);
print((value as ParseObject).updatedAt);
print((value as ParseObject).createdAt);
print((value as ParseObject).get('objectId'));
print((value as ParseObject).get('updatedAt'));
print((value as ParseObject).get('createdAt'));
});
}
void main() async{
Parse().initialize(
kParseApplicationId,
kParseServerUrl,
masterKey: kParseMasterKey,
clientKey: kParseClientKey,
debug: true,
liveQueryUrl: kLiveQueryUrl,
autoSendSessionId: true,
);
var dietPlan = ParseObject('DietPlan')
..set('Name', 'Ketogenic')
..set('Fat', 65);
await dietPlan.save();
var response = await dietPlan.save();
if (response.success) {
dietPlan = response.result;
}
final LiveQuery liveQuery = LiveQuery();
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('DietPlan'))
..whereEqualTo('intNumber', 1);
Subscription subscription = await liveQuery.client.subscribe(query);
subscription.on(LiveQueryEvent.delete, (value) {
print('*** DELETE ***: ${DateTime.now().toString()}\n $value ');
print((value as ParseObject).objectId);
print((value as ParseObject).updatedAt);
print((value as ParseObject).createdAt);
print((value as ParseObject).get('objectId'));
print((value as ParseObject).get('updatedAt'));
print((value as ParseObject).get('createdAt'));
});
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
supportedLocales: [const Locale('pt', 'BR')],
home: Home()
);
}
}
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
var _listTiles = List<String>();
Color _iconColor = Colors.black;
Color standardIconColor = Colors.black;
Color alternateIconColor = Colors.green;
TextEditingController _nameController = TextEditingController();
var _scaffoldKey = new GlobalKey<ScaffoldState>();
String standardTileTitle = "Adicione um horário disponível...";
String _titleTile = "Adicione um horário disponível...";
String _tileSubtitle = "Edite o nome do funcionário...";
int _selectedIndexBottomNavBar = 0;
final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey = new GlobalKey<RefreshIndicatorState>();
#override
Widget build(BuildContext context) {
return RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: () async {
setState(() {
});},
child: Scaffold(
key: _scaffoldKey,
floatingActionButton: FloatingActionButton(
onPressed: () {
invokeDatePicker();
},
child: Icon(Icons.add),
),
bottomNavigationBar: bottomNavigationBar(),
appBar: AppBar(
title: Text('Marca Horário'),
),
// body: Center(
// child: Text('Hello World'),
body: bodyStartScreen()
),
);
}
Widget bottomNavigationBar(){
void _onItemTapped(int index) {
setState(() {
_selectedIndexBottomNavBar = index;
});
print(_selectedIndexBottomNavBar);
if(_selectedIndexBottomNavBar == 2){
DatePicker.showDateTimePicker(context,
showTitleActions: true,
minTime: DateTime(2020, 1, 1),
maxTime: DateTime(2021, 12, 31),
onChanged: (date) {
print('change $date');
},
onConfirm: (date) {
print('confirm $date');
_listTiles.add(DateFormat.yMMMEd('pt_BR').add_Hm().format(date).toString());
_titleTile = DateFormat.yMMMEd('pt_BR').add_Hm().format(date).toString();
setState(() {
});
},
currentTime: DateTime.now(),
locale: LocaleType.pt);
}
}
return BottomNavigationBar(
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: 'Home',
),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: 'Funcionário',
),
BottomNavigationBarItem(
icon: Icon(Icons.access_time),
label: 'Marcar',
),
],
currentIndex: _selectedIndexBottomNavBar,
selectedItemColor: Colors.amber[800],
onTap: _onItemTapped,
);
}
Widget bodyStartScreen(){
return Column(
children: [
//the main title of the screen
Padding(
padding: EdgeInsets.all(16.0),
child: Text("Horários Possíveis",
style: TextStyle(
fontSize: 18.0
),
),
),
//gets available employees and datetimes from the server
FutureBuilder(builder: (context,snapshot){
if (snapshot.data != null) {
List<Data> dataList = snapshot.data;
return Expanded(
child: ListView.builder(
itemBuilder: (_, position) {
return Card(
child: ListTile(
title: Text(dataList[position].dateTime),
subtitle: Text(dataList[position].employee),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(icon: Icon(Icons.edit), onPressed: () {
//Show dialog box to update item
showUpdateDialog(dataList[position]);
}),
IconButton(icon: Icon(Icons.check_circle, color: Colors.green,), onPressed: () {
}),
//Show dialog box to delete item
IconButton(icon: Icon(Icons.delete), onPressed: () {
deleteData(dataList[position].objectId);
}),
],
),
),
);
},
itemCount: dataList.length,
),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
future: getDataList(),
),
Divider(
color: Colors.black,
),
scheduleTile()
],
);
}
void invokeDatePicker(){
DatePicker.showDateTimePicker(context,
showTitleActions: true,
minTime: DateTime(2020, 1, 1),
maxTime: DateTime(2021, 12, 31),
onChanged: (date) {
print('change $date');
},
onConfirm: (date) {
print('confirm $date');
_listTiles.add(DateFormat.yMMMEd('pt_BR').add_Hm().format(date).toString());
_titleTile = DateFormat.yMMMEd('pt_BR').add_Hm().format(date).toString();
setState(() {
});
},
currentTime: DateTime.now(),
locale: LocaleType.pt);
}
void showUpdateDialog(Data data) {
_nameController.text = data.employee;
showDialog(context: context,
builder: (_) => AlertDialog(
content: Container(
width: double.maxFinite,
child: TextField(
controller: _nameController,
decoration: InputDecoration(
labelText: "Atualizar funcionário disponível",
),
),
),
actions: <Widget>[
FlatButton(onPressed: () {
Navigator.pop(context);
data.employee = _nameController.text;
updateData(data);
}, child: Text("Atualizar")),
FlatButton(onPressed: () {
Navigator.pop(context);
}, child: Text("Cancelar")),
],
)
);
}
Widget scheduleTile(){
return Padding(
padding: EdgeInsets.only(bottom: 80.0),
child: Card(
color: Colors.grey,
child: ListTile(
title: Text(_titleTile),
subtitle: Text(_tileSubtitle),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.edit,
color: standardIconColor,
size: 20.0,
),
onPressed: () {
setState(() {
employeeAvailable();
});
},
),
IconButton(
icon: Icon(
Icons.check_circle_outline,
color: _iconColor,
size: 20.0,
),
onPressed: () {
setState(() {
(_titleTile != standardTileTitle) ? confirmSchedule() : fillTimeDialog();
});
},
)
],
),
),
),
);
}
void fillTimeDialog(){
showDialog(context: context,
builder: (_) => AlertDialog(
content: Container(
width: double.maxFinite,
child: Text("Insira o horário disponível!"),
),
actions: <Widget>[
FlatButton(onPressed: () {
Navigator.pop(context);
}, child: Text("OK")),
],
)
);
}
void employeeAvailable(){
showDialog(context: context,
builder: (_) => AlertDialog(
content: Container(
width: double.maxFinite,
child: TextField(
controller: _nameController,
decoration: InputDecoration(
labelText: "Funcionário",
),
),
),
actions: <Widget>[
FlatButton(onPressed: () {
Navigator.pop(context);
//addTodo();
setState(() {
_tileSubtitle = "Disponível: " + _nameController.text;
});
}, child: Text("Inserir")),
FlatButton(onPressed: () {
Navigator.pop(context);
setState(() {
_tileSubtitle = " ";
});
}, child: Text("Desfazer")),
],
)
);
}
void confirmSchedule(){
showDialog(context: context,
builder: (_) => AlertDialog(
content: Container(
width: double.maxFinite,
child: Text("Confirma disponibilidade?")
),
actions: <Widget>[
FlatButton(onPressed: () {
Navigator.pop(context);
//addTodo();
addData();
setState(() {
_iconColor = alternateIconColor;
_tileSubtitle = "Disponível: " + _nameController.text;
});
}, child: Text("Confirma")),
FlatButton(onPressed: () {
Navigator.pop(context);
setState(() {
_iconColor = standardIconColor;
_tileSubtitle = " ";
});
}, child: Text("Não")),
],
)
);
}
void addData() {
_scaffoldKey.currentState.showSnackBar(SnackBar(content: Row(
children: <Widget>[
Text("Adicionando informações..."),
CircularProgressIndicator(),
],
mainAxisAlignment: MainAxisAlignment.spaceBetween,
),
duration: Duration(minutes: 1),
));
Data data = Data(employee: _tileSubtitle, dateTime: _titleTile);
DataUtils.addData(data)
.then((res) {
_scaffoldKey.currentState.hideCurrentSnackBar();
Response response = res;
if (response.statusCode == 201) {
//Successful
_nameController.text = "";
_scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("Informações disponibilizadas!"), duration: Duration(seconds: 1),));
setState(() {
//Update UI
});
}
});
}
void deleteData(String objectId) {
_scaffoldKey.currentState.showSnackBar(SnackBar(content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("Excluindo disponibilidade..."),
CircularProgressIndicator(),
],
),
duration: Duration(minutes: 1),
),);
DataUtils.deleteData(objectId)
.then((res) {
_scaffoldKey.currentState.hideCurrentSnackBar();
Response response = res;
if (response.statusCode == 200) {
//Successfully Deleted
_scaffoldKey.currentState.showSnackBar(SnackBar(content: (Text("Disponibilidade excluída!")),duration: Duration(seconds: 1),));
setState(() {
});
} else {
//Handle error
}
});
}
void updateData(Data data) {
_scaffoldKey.currentState.showSnackBar(SnackBar(content: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text("Atualizando disponibilidade..."),
CircularProgressIndicator(),
],
),
duration: Duration(minutes: 1),
),);
DataUtils.updateData(data)
.then((res) {
_scaffoldKey.currentState.hideCurrentSnackBar();
Response response = res;
if (response.statusCode == 200) {
//Successfully Deleted
_nameController.text = "";
_scaffoldKey.currentState.showSnackBar(SnackBar(content: (Text("Disponibilidade atualizada!"))));
setState(() {
});
} else {
//Handle error
}
});
}
Future <List<Data>> getDataList() async{
List<Data> dataList = [];
Response response = await DataUtils.getDataList();
print("Code is ${response.statusCode}");
print("Response is ${response.body}");
if (response.statusCode == 200) {
var body = json.decode(response.body);
var results = body["results"];
for (var data in results) {
dataList.add(Data.fromJson(data));
}
} else {
//Handle error
}
return dataList;
}
}
data_utils.dart:
import 'dart:convert';
import 'package:http/http.dart';
import 'package:marca_horario/model/data.dart';
import 'package:marca_horario/constants.dart';
class DataUtils {
//static final String _baseUrl = "https://parseapi.back4app.com/classes/";
static final String _baseUrl = baseUrl;
//CREATE
static Future<Response> addData(Data data) async {
String apiUrl = _baseUrl + "Data";
Response response = await post(apiUrl,
headers: {
'X-Parse-Application-Id': kParseApplicationId,
'X-Parse-REST-API-Key': kParseRestApiKey,
'Content-Type': 'application/json'
},
body: json.encode(data.toJson()),
);
return response;
}
//READ
static Future getDataList() async{
String apiUrl = _baseUrl + "Data";
Response response = await get(apiUrl, headers: {
'X-Parse-Application-Id' : kParseApplicationId,
'X-Parse-REST-API-Key' : kParseRestApiKey,
});
return response;
}
//UPDATE
static Future updateData(Data data) async{
String apiUrl = _baseUrl + "Data/${data.objectId}";
Response response = await put(apiUrl, headers: {
'X-Parse-Application-Id' : kParseApplicationId,
'X-Parse-REST-API-Key' : kParseRestApiKey,
'Content-Type' : 'application/json',
},
body: json.encode(data.toJson())
);
return response;
}
//DELETE
static Future deleteData(String objectId) async{
String apiUrl = _baseUrl + "Data/$objectId";
Response response = await delete(apiUrl, headers: {
'X-Parse-Application-Id' : kParseApplicationId,
'X-Parse-REST-API-Key' : kParseRestApiKey,
});
return response;
}
}
Problem solved!
My problem was:import 'package:parse_server_sdk/parse_server_sdk.dart';
It needs to be replaced by:import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
and in pubspec.yaml it's necessary to use only the correspondent dependency:
parse_server_sdk_flutter: ^2.0.1

Hello does someone successfully added Bluetooth CPCL on your Flutter project?

I tried to print in a ZEBRA ez320 Bluetooth printer I found out that it only works using cpcl code, the good news is I have the sample code for cpcl but I don't know how to integrate it on my Flutter project.
Can someone help me out?
This github https://github.com/devinim/flutter-zsdk has Zebra SDK Kit Flutter Integration
code snippet
devices = await FlutterZsdk.discoverBluetoothDevices();
devices.forEach((d) {
print('Device: ${d.friendlyName} [${d.mac}]');
});
...
d.sendZplOverBluetooth(FLUTTER_LOGO_ZPL),
full example code https://github.com/devinim/flutter-zsdk/blob/master/example/lib/main.dart
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_zsdk/flutter_zsdk.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
static const String FLUTTER_LOGO_ZPL = "^XA^CI28^PON^PW560^MNN^LL460^LH0,0^LT60" +
"^GFA,6150,6150,25,,gP01XFCgP03XF8gP07XF,gP0XFE,gO01XFC,gO03XF8,gO07XF,gO0XFE,gN01XFC,gN03XF8,gN0YF,gN0XFE,gM01XFC,gM03XF8,gM0YF,gL01XFE"
",gL01XFC,gL07XF8,gL0YF,gK01XFE,gK03XFC,gK07XF8,gK0YF,gJ01XFE,gJ03XFC,gJ07XF8,gJ0YF,gI01XFE,gI03XFC,gI07XF8,gI0YF,gH01XFE,gH03XFC,gH07XF8"
",gH0YF,gG01XFE,gG03XFC,gG07XF8,gG0YF,g01XFE,g03XFC,g07XF8,g0YF,Y01XFE,Y03XFC,Y07XF8,Y0YF,X01XFE,X03XFC,X07XF8,X0YF,W01XFE,W03XFC,W07XF8"
",W0YF,V01XFE,V03XFC,V07XF8,V0YF,U01XFE,U03XFC,U07XF8,U0YF,T01XFE,T03XFC,T07XF8,T0YF,S01XFE,S03XFC,S07XF8,S0YF,R01XFE,R03XFC,R07XF8"
",R0YF,Q01XFE,Q03XFC,Q07XF8,Q0YF,P01XFE,P03XFC,P07XF8,P0YF,O01XFE,O03XFC,O07XF8,O0YF,N01XFE,N03XFC,N07XF8,N0YF,M01XFE,M03XFC,M07XF8"
",M0YF,L01XFE,L03XFC,L07XF8,L0YF,K01XFE,K03XFC,K07XF8,K0YF,J01XFE,J03XFC,J07XF8,J0YF,I01XFE,I03XFC,I07XF8,I0XFE,001XFE,003XFCN03XF"
",007XF8N07XF,00XFEO0XFE,01XFCN01XFC,03XFCN03XF8,07XF8N07XF,0XFEO0XFE,1XFCN01XFC,3XF8N03XF8,7XF8N07XF,3WFEO0XFE,1WFCN01XFC,0WF8N03XF8"
",07VFO07XF,03UFEO0XFE,01UFCN01XFC,00UF8N03XF8,007TFO07XF,003SFEO0XFE,001SFCN01XFC,I0SF8N03XF8,I07RFO07XF,I03QFEO0XFE,I01QFCN01XFC,J0QF8N03XF8"
",J07PFO07XF,J03OFEO0XFE,J01OFCN01XFC,K0OF8N03XF8,K07NFO07XF,K03MFEO0XFE,K01MFCN01XFC,L0MF8N03XF8,L07LFO07XF,L03KFEO0XFE,L01KFCN01XFC,M0KF8N03XF8"
",M07JFO07XF,M03IFEO0XFE,M01IFCN01XFC,N0IF8N03XF8,N07FFO07XF,N03FEO0XFE,N01FCN01XFC,O0F8N03XF8,O07O07XF,O02O0XFE,X01XFC,X03XF8,X07XF,X0XFE,W01XFC"
",W03XF8,W07XF,W0XFE,V01XFC,V03XF8,V07XF,V0XFE,U01XFC,U03XF8,U07XF,U0XFE,T01XFC,T07XF8,T07XF,T03XF8,T01XFC,U0XFE,U07XF,U03XF8,U01XFC,V0XFE,V07XF"
",V03XF8,V01XFC,W0XFE,W07XF,W07XF8,W03XFC,X0XFE,X07XF,X07XF8,X03XFC,X01XFE,Y07XF,Y07XF8,Y03XFC,Y01XFE,g0YF,g07XF8,g03XFC,g01XFE,gG0YF,gG07XF8,gG03XFC"
",gG01XFE,gH0YF,gH07XF8,gH03XFC,gH01XFE,gI0YF,gI07XF8,gI03XFC,gI01XFC,gJ0XFE,gJ07XF,gJ03XF8,gJ01XFC,gK0XFE,gK07XF,gK03XF8,gK01XFC,gL0XFE,gL07XF,gL03XF8"
",gL01XFC,gM0XFE,gM07XF,gM03XF8,gM01XFC,gN0XFE,gN07XF,gN03XF8,gN01XFC,gO0XFE,gO07XF,gO03XF8,gO01XFC,gP0XFE,gP07XF,gR01VF8"
",^FS ^XZ";
List<ZebraBluetoothDevice> _devices = List();
#override
void initState() {
super.initState();
__init();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> __init() async {
List<ZebraBluetoothDevice> devices = List();
try {
devices = await FlutterZsdk.discoverBluetoothDevices();
devices.forEach((d) {
print('Device: ${d.friendlyName} [${d.mac}]');
});
} catch (e) {
showDialog(context: context, child: Text(e));
//throw e;
print('Error' + e);
}
if (!mounted) return;
setState(() {
_devices = devices;
});
}
String levelText = "Querying...";
_level(ZebraBluetoothDevice d) {
d.batteryLevel().then((t) {
setState(() {
levelText = t;
});
});
}
Widget _listPrinters() {
List<Widget> items = List();
if (_devices.length < 1) {
items.add(ListTile(
title: Text("Not found any or still searching"),
));
} else {
items.addAll([
ListTile(
title: Text("Found ${_devices.length} device(s)"),
),
SizedBox(height: 50),
]);
_devices.forEach((d) {
_level(d);
items.add(
ListTile(
title: Text(d.friendlyName),
subtitle: Text(d.mac + "[%${levelText}]"),
leading: IconButton(icon: Icon(Icons.list), onPressed: () => d.properties()),
trailing: IconButton(
icon: Icon(Icons.print),
onPressed: () => d.sendZplOverBluetooth(FLUTTER_LOGO_ZPL),
),
),
);
});
}
return ListView(
padding: EdgeInsets.all(24),
children: items,
);
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Zebra Plugin Example App'),
),
body: _listPrinters(),
),
);
}
}

Resources