How to send flutter search to Firebase Analytics event - firebase

Is there a way to catch only the final search to send in Firebase Analytics event?
_searchPressed() {
final FirebaseAnalytics analytics = FirebaseAnalytics.instance;
setState(() {
if (this._searchIcon.icon == Icons.search) {
this._searchIcon = new Icon(Icons.close);
this._appBarTitle = TextField(
decoration: new InputDecoration(
prefixIcon: new Icon(Icons.search),
),
onChanged: (text) {
text = text.toLowerCase();
setState(() {
_itemForDisplay = _item.where((item) {
var itemTitle = item.code.toLowerCase();
return itemTitle.contains(text);
}).toList();
if (text.length > 6) {
analytics.logEvent(
name: text.replaceAll(' ', ''),
parameters: {'term': text.replaceAll(' ', ''), 'vaule': 1});
//space not allowed in firebase
}
});
},
);
} else {
this._searchIcon = new Icon(Icons.search);
this._appBarTitle = new Text( 'Search bar' );
analytics.logEvent(
name: 'search_done',
parameters: {'term': "close", 'vaule': 1});
_itemForDisplay = _item;
}
});
}
A satisfactory solution is to capture the search when closing search is selected. The search is done simultaneously so there is no information when the search is completed. The only information about the end of the search is when you touch the close icon. Another option would be the longest search text.

Related

getx and weird problem: text won't change on UI

I am trying to create a search function from firebase
and i am using getx for the state management
I counter this weird problem, when I try to use the function directly it work, but when I use the firebase function it does not.
I will explain more on the code snipit.
this is my search textForm
TextFormField(
onChanged: (value) => database.searchForId(value),
decoration: InputDecoration(
focusColor: Colors.lightGreenAccent,
prefixIcon: Icon(
Icons.search,
color: Colors.lightGreenAccent,
),
labelText: 'Search...',
labelStyle: TextStyle(color: Colors.lightGreenAccent),
border: OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide:
const BorderSide(color: Colors.lightGreenAccent),
),
)),
as you can see I am using on change value
database code for search
searchForId(searchText) {
FirebaseFirestore.instance
.collection('inventory')
.where('id', isGreaterThanOrEqualTo: searchText)
.get()
.then((snapshot) {
var data = snapshot.docs[0];
if (data['id'] != searchText) {
return;
} else {
searchController.changeId(data['id']);
searchController.changeName(data['name']);
searchController.changeQuantity(data['quantity']);
searchController.changeCost(data['cost']);
searchController.changeMyPrice(data['myPrice']);
}
});
}
and the controller
class SearchController extends GetxController {
var id = 'No info'.obs;
var name = 'No info'.obs;
var quantity = 'No info'.obs;
var cost = 'No info'.obs;
var myPrice = 'No info'.obs;
changeId(_id) {
id(_id);
print(id);
}
the print show me that the value is changed, but never update it on my UI
Obx(() => Text(searchController.id.value))
so i trid to call the controller dirctly (not using database file) and it work just fine, and UI updated
any idea why ?
the weird is the print show me the function get called and working fine ( i mean when i use database file as well ) but it never update the UI
I think u forgot to keep
SearchController searchcontroller = Get.put(SearchController());
if you initialized it before just find that controller using
SearchController searchcontroller = Get.find();
var id = 'No info'.obs;
searchController.changeId(data['id']);
changeId(_id) {
id(_id);
print(id);
}
In order to update your text in UI, you can either update your code
changeId(_id) {
id(_id);
print(id);
update(); // Calling update manually. update() is a function in GetxController to trigger the observable change.
}
or
changeId(String _id) {
id.value = _id;
}

Update Firebase with the final score and display to user

I'm getting the score and topicTotal from the state and printing them, however, I want to update Firebase with the topicTotal by retrieving topicTotal in report. Every way I try to access topicTotal in report, I get null in Firebase or The getter 'topicTotal' was called on null.
How can I access topicTotal in report and update Firebase with it? Also, how can I display topicTotal to the user?
set score(Options newValue) {
var score = idx += newValue.score;
_score = newValue;
print(score);
_score = newValue;
notifyListeners();
}
set topicTotal(Options newValue) {
var topicTotal = idx;
_score = newValue;
this._topicTotal = newValue;
print(topicTotal);
notifyListeners();
}
model
class Report {
...
int topicTotal;
Report({ this.uid, this.topics, this.email, this.total, this.topicTotal, this.level, this.id, this.title, this.description, this.img });
factory Report.fromMap(Map data) {
return Report(
uid: data['uid'],
email: data['email'],
total: data['total'] ?? 0,
topics: data['topics'] ?? {},
topicTotal: data['topicTotal'] ?? 34,
level: data['level'] ?? 383,
id: data['id'] ?? '',
title: data['title'] ?? '',
description: data['description'] ?? '',
img: data['img'] ?? 'default.png',
);
}
}
Here I can access total but not topicTotal
if (report != null)
Text('${report.total ?? 0}',
style: Theme.of(context).textTheme.display3),
Text('Assessments Completed',
style: Theme.of(context).textTheme.subhead),
Spacer(),
if (report != null)
Text('your topic score is ${report.topicTotal ?? 0}'),
onPressed: () {
_updateUserReportWithTopics(assessment, state, optionSelected);
Future<void> _updateUserReportWithTopics(Assessment assessment, AssessmentState state, Options optionSelected) {
state.topicTotal = optionSelected;
return Global.reportRef.upsert(
({
'total': FieldValue.increment(1),
'topics': {
'${assessment.topic}': FieldValue.arrayUnion([
assessment.title,
assessment.topicTotal,
assessment.questions.length,
]),
},
//'topicTotal': state.topicTotal = optionSelected
}),
);
}

Flutter: How to remove awaits to take advantage of Firebase Offline Persistence?

I'm using a drop-down list (DropDown), whose elements are obtained from Firebase. The form works right, however when the internet connection is lost the Firebase Offline Persistence property doesn't work and the CircularProgressIndicator stays active. Reading some responses such as Using Offline Persistence in Firestore in a Flutter App, it is indicated that awaits should not be handled, however it is not clear to me how to achieve it:
class EstanqueAlimentarPage extends StatefulWidget {
#override
_EstanqueAlimentarPageState createState() => _EstanqueAlimentarPageState();
}
class _EstanqueAlimentarPageState extends State<EstanqueAlimentarPage> {
final formKey = GlobalKey<FormState>();
AlimentoBloc alimentoBloc = new AlimentoBloc();
AlimentoModel _alimento = new AlimentoModel();
AlimentarModel alimentar = new AlimentarModel();
List<AlimentoModel> _alimentoList;
bool _alimentoDisponible = true;
#override
void dispose() {
alimentoBloc.dispose();
super.dispose();
}
#override
void initState() {
_obtenerListaAlimentoUnaVez();
super.initState();
}
Future<void> _obtenerListaAlimentoUnaVez() async {
_alimentoList = await alimentoBloc.cargarAlimento(idEmpresa); // Await that I want to eliminate
if (_alimentoList.length > 0) { // Here appears a BAD STATE error when the internet connection goes from off to on
_alimento = _alimentoList[0];
_alimentoDisponible = true;
} else {
_alimentoDisponible = false;
}
_cargando = false;
setState(() {});
}
#override
Widget build(BuildContext context) {
return Form(
key: formKey,
child: Column(
children: <Widget> [
_crearTipoAlimento(_alimentoList),
SizedBox(height: 8.0),
_crearComentarios(),
]
)
),
_crearBoton('Guardar'),
}
Widget _crearTipoAlimento(List<AlimentoModel> lista) {
return Container(
decoration: _cajaBlanca,
child:
!_cargando // If it isn't loading, Dropdown must be displayed
? DropdownButtonFormField<AlimentoModel>(
decoration: InputDecoration(
labelText: 'Nombre del Alimento',
contentPadding: EdgeInsets.only(top:5.0),
prefixIcon: Icon(FontAwesomeIcons.boxOpen, color: Theme.of(context).primaryColor,),
border: InputBorder.none,
),
value: _alimento,
items: lista.map((AlimentoModel value) {
return DropdownMenuItem<AlimentoModel>(
child: Text(value.nombre),
value: value,
);
}).toList(),
onChanged: (_alimentoDisponible) ? (AlimentoModel _alimentoSeleccionado) {
print(_alimentoSeleccionado.nombre);
_alimento = _alimentoSeleccionado;
setState(() {});
} : null,
disabledHint: Text('No hay Alimento en Bodega'),
onSaved: (value) {
alimentar.idAlimento = _alimento.idAlimento;
alimentar.nombreAlimento = _alimento.nombreRef;
}
)
: Center (child: CircularProgressIndicator(strokeWidth: 1.0,))
);
}
Widget _crearComentarios() {
return TextFormField(
// -- DESIGN OTHER FIELDS -- //
onSaved: (value) {
alimentar.comentarios = value;
}
),
);
}
Widget _crearBoton(String texto) {
return RaisedButton(
// -- DESIGN -- //
onPressed: (_guardando) ? null : _submit,
),
);
}
void _submit() {
// CODE TO WRITE FORM IN FIREBASE
}
}
The function code from my BLOC is:
Future<List<AlimentoModel>> cargarAlimento(String idEmpresa, [String filtro]) async {
final alimento = await _alimentoProvider.cargarAlimento(idEmpresa, filtro); //It's one await more
_alimentoController.sink.add(alimento);
return alimento;
}
And the Query from PROVIDER is:
Future<List<AlimentoModel>> cargarAlimento(String idEmpresa, [String filtro]) async {
Query resp;
final List<AlimentoModel> alimento = new List();
resp = db.child('empresas').child(idEmpresa).child('bodega/1').child('alimento')
.orderByChild('cantidad').startAt(0.000001);
return resp.once().then((snapshot) {
if (snapshot.value == null) return [];
if (snapshot.value['error'] != null) return [];
snapshot.value.forEach((id, alim){
final temp = AlimentoModel.fromJson(Map<String,dynamic>.from(alim));
temp.idAlimento = id;
alimento.add(temp);
});
return alimento;
});
When using Firebase offline, you omit the await only on things that change the server (e.g., creating or updating a record). So you won't wait for the server to say "yes I wrote it", you assume that it's written.
In your case, however, you are not writing data, you are reading data. You will have to keep await in your example. The way you load your data has orderByChild and startAt, maybe those are preventing offline loading. Normally, you get it if it's already in the cache: https://firebase.google.com/docs/firestore/manage-data/enable-offline#get_offline_data
You mention a BAD STATE error, maybe if you provide that, we may be able to pinpoint the issue a bit better.

Cannot recognize Text when using Firebase_ml_vision kit from camera

Im trying to build an app that uses the Text Recognition that comes with the Firebase_ml_vision package. When i use the Firebase.VisionImage.fromFile(pickedImage) and i download a picture from the internet with text it works perfectly, but when i try to use an image taken with my iPhone Xr camera it does not read the text decently. The output is similar to this:
flutter: 2000
AADAT4A
flutter: 2000
AADAT4A
flutter: 20000
flutter: 03012
flutter: 2000
this happens when i try to print blocks.text lines or elements.
Does anyone have an ideia how to implement this?
I used this code: (its not all the code but the necessary)
File pickedImage;
List<String> words = [];
Future pickImage() async {
var tempStore =
await ImagePicker.pickImage(source: ImageSource.gallery); //gallery here
setState(() {
pickedImage = tempStore;
isImageLoaded = true;
});
}
Future pickCameraImage() async {
var tempStore =
await ImagePicker.pickImage(source: ImageSource.camera); //camera here
setState(() {
pickedImage = tempStore;
isImageLoaded = true;
});
}
Future readText() async {
FirebaseVisionImage ourImage = FirebaseVisionImage.fromFile(pickedImage);
TextRecognizer recognizeText = FirebaseVision.instance.textRecognizer();
VisionText readText = await recognizeText.processImage(ourImage);
for (TextBlock block in readText.blocks) {
// final Rect boundingBox = block.boundingBox;
// final List<Offset> cornerPoints = block.cornerPoints;
// final String text = block.text;
// final List<RecognizedLanguage> languages = block.recognizedLanguages;
for (TextLine line in block.lines) {
print(block.text);
for (TextElement word in line.elements) {
words.add(word.text);
}
}
} isTextLoaded = true;
}
In the Ui part:
(...)
RaisedButton(
child: Text('Pick an Image'),
onPressed: pickImage,
),
SizedBox(
height: 10.0,
),
RaisedButton(
child: Text('Pick a Camera Image'),
onPressed: pickCameraImage,
),
SizedBox(
height: 10.0,
),
RaisedButton(
child: Text('Read Text'),
onPressed: readText,
),
(...)
I do not have any errors. Anyone with more experience can help me? Thank you

Flutter Enable/Disable Button based on TextFormField content

How can I activate/deactivate a button based on the content of a TextFormField?
I want to make sure a user can only press Button X if the TextFormField has 10 entered numbers (if it's a mobile number).
Thanks!
The state of the widget can be updated in the Form's onChanged callback which is called whenever any of the form fields' value changes. There you can use a form key to validate the form and set a flag to enable/disable the button. This solution allows you to scale to disable buttons in forms with multiple fields. For example,
/// Key used to reference the form.
final _formKey = GlobalKey<FormState>();
...
Form(
key: _formKey,
onChanged: () => setState(() => _enableBtn = _formKey.currentState.validate()),
child: ListView(
children: <Widget>[
TextFormField(
validator: (value) => value.length < 10 ?
'Number must be at least 10 digits' : // return an error message
null,
...
),
],
),
)
...
FlatButton(
onPressed: _enableBtn ?
() => _doSomething() :
null, // setting onPressed to null disables the button.
...
A simple way would be to set the autovalidate property of our TextFormField to true. This will automatically detect for changes on our TextFormField widget. We can then try to check if our TextFormField's value has a String length of 10 characters on the validator property . After that we can call setState to enable or disable our button (I use FlatButton in this example).
bool _btnEnabled = false;
...
#override
Widget build(BuildContext context){
...
TextFormField(
...
autovalidate: true,
validator: (String txt){
if (txt.length == 10){
setState((){
_btnEnabled = true;
});
} else {
setState((){
_btnEnabled = false;
});
}
}
...
FlatButton(
onPressed: _btnEnabled == true ? yourCallback : null,
child: ...
you can use Flutter Reactive Forms. It's a model-driven approach to handling Forms inputs and validations, heavily inspired in Angular's Reactive Forms.
It's a very simple to use libray and in the documentation there is a section that explains how to Enable/Disable Submit button based on the validity of the entire form, not just a field.
The accepted answer seems to not work using the latest Flutter v1.7.8 (stable), it gives me the following error:
This TestForm widget cannot be marked as needing to build because the framework is already in the process of building widgets
The working version looks like the following:
...
autovalidate: true,
validator: (String txt){
bool isValid = txt.length == 10;
if (isValid != _btnEnabled) {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
_btnEnabled = txt.length == 10;
});
});
}
}
...
You can use this method .
bool isEnable = false;
void validateButton() {
bool isValid = true;
isValid = userEmail.isNotEmpty &&
userPassword.isNotEmpty &&
validateEmail(userEmail) &&
userPassword.length >= 8;
setState(() {
isEnable = isValid;
});
}
now in your Textfield onChanged method you have to call this function
like This
onChanged: (email) {
userEmail = email;
setState(() {});
validateButton();
},
and in your Login Button
isEnable?ActiveButton():DisableButton()
Get error "setState() or markNeedsBuild() called during build." with accepted answer. Just add Future.delayed(Duration.zero).then(....) as a trick solution, it's working on flutter 1.12.13
TextFormField(
...
autovalidate: true,
validator: (String txt){
if (txt.length == 10){
Future.delayed(Duration.zero).then((_){
setState((){
_btnEnabled = true;
});
});
} else {
Future.delayed(Duration.zero).then((_){
setState((){
_btnEnabled = false;
});
});
}
}
....

Resources