I have created a simple home automation project with flutter and ESP32, On my App I have a bunch of buttons that will change the state of the variables inside the firestore if they are pressed or not. If pressed send True, if not false. I'm trying to make this happen but I can't seem to get it right.
Inside the onPressed is what I have tried, all of the code is inside a Stateful class:
bool pressed = false;
Random no effect code...
CustomButton(
icon: Icons.lightbulb_outline,
text: 'On',
text2: 'Lâmpada 1\nSchuma',
onPressed: () => (){
dynamic databaseReference = Firestore.instance.collection('sensores').where('Led1','==',pressed);
Firestore.instance.runTransaction((transaction) async {
await transaction.update(
documentReference, _value);
};
},
),
SizedBox(width: 30.0),
CustomButton(
icon: Icons.lightbulb_outline,
text: 'On',
text2: 'Lâmpada 2\nSchuma',
onPressed: () => (){
},
My firestore:
Second try:
CustomButton(
icon: Icons.lightbulb_outline,
text: 'On',
text2: 'Lâmpada 1\nSchuma',
onPressed: () => (){
Firestore.instance.collection("sensores").document("2KbeT....").updateData({
'Led1': true,
});
},
),
The problem, was the button formating. The code send by Uni works great.
First add firebase to your app: https://firebase.google.com/docs/flutter/setup?platform=ios
(Import Firestore)
You can simply update your data:
Firestore.instance.collection('YourCollection').document('YourDocument').updateData({
'Led1': true,
});
To Fetch your data from the server:
await Firestore.instance
.collection('YourCollection')
.document('YourDocument')
.get()
.then((DocumentSnapshot ds) {
led1 = ds["Led1"];
led2 = ds["Led2"];
led3 = ds["Led3"];
});
I would recommend using the MQTT Protocol to communicate to your ESP32 instead of storing it in firestore. Note that firestore allows you to have 20k reads and 50k writes per day so if you have devices that need more than that it would be impractical to use firestore as a communication method.
Related
I have a webview on a Pageview who display a chat from a plugin that I use on my wordpress website (I have no access to data from this plugin). It's not a chat with FB or google account, it's only an open chat room, where users can add and save her nickname (I suppose nickname is stored in cookies ?). As long as the webview is active the nickname remains memorized. Problem, after each time the app is close and reopen, the user lose his nickname.
Here is my code
WebView(
initialUrl: 'https://XXXX',
javascriptMode: JavascriptMode.unrestricted,
gestureRecognizers: [
Factory(() => PlatformViewVerticalGestureRecognizer()),
].toSet(),
),
How can I save session ? Even when after app is close and reopen ?
First, in your website project, add this javascript code which it will be accessible to the HTML pseodo input:
var psuedoInput = document.querySelector('inputSelectorHere');
_selector.addEventListener('change', function(event) {
var message = psuedoInput.value;
if (messageHandler) {
messageHandler.postMessage(message);
}
});
you can add it inside a <script></script> in the .html file or in a .js separate file.
this basically will post a message with the pseudo input value to our app later.
Don't forget to change inputSelectorHere with your psuedo input selector.
now in your flutter code, create a simple Stirng variable like this:
String? cookie;
then in the WebView widget:
WebView(
javascriptChannels: <JavascriptChannel>[
// javascript channel that saves the cookie
JavascriptChannel(
name: 'Cookie',
onMessageReceived: (JavascriptMessage message) {
cookie = message.message;
print("cookie: $cookie");
},
),
].toSet(),
onWebViewCreated: (controller) {
if (cookie == null) {
return;
}
controller.runJavascript("document.cookie = '$cookie';");
// }
},
initialCookies: [],
initialUrl: 'https://XXXX',
javascriptMode: JavascriptMode.unrestricted,
),
here the JavascriptChannel is set so it receives those messages which will be sent from your website from the webview, then it will be saved inside the cookie variable which we created.
when you close the webview and open it again, the onWebViewCreated will be called, and the cookie now is not null, so it will assign the cookie we saved to document.cookie in the webview.
As I can understand. You just need to get cookies (or cache and local storage) and store them in FlutterSecureStorage. when the user closes the app and re-opens just check if cookies are stored in FlutterSecureStorage or not.
If Cookies are present just add the cookies and refresh the page. I have written a pseudo code for the demo purpose (Code might not work as you expected but it will give you a brief idea about my approach).
I have added a code for the cookies. I also added code for the cache and local storage but you have to configure it according to your needs.
Please read the comments.
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:webview_flutter/webview_flutter.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const FlutterWebViewDemo());
}
class FlutterWebViewDemo extends StatefulWidget {
const FlutterWebViewDemo({Key? key}) : super(key: key);
#override
State<FlutterWebViewDemo> createState() => _FlutterWebViewDemoState();
}
class _FlutterWebViewDemoState extends State<FlutterWebViewDemo> {
late final WebViewCookieManager cookieManager = WebViewCookieManager();
var controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {
// Update loading bar.
},
onPageStarted: (String url) {},
onPageFinished: (String url) {},
onWebResourceError: (WebResourceError error) {},
),
)
..loadRequest(Uri.parse(''));
/// <---- please add the url here
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
children: [
Expanded(child: WebViewWidget(controller: controller)),
ElevatedButton(
onPressed: () async {
_onListCache();
},
child: const Text("Save Cache"),
),
ElevatedButton(
onPressed: () async {
_onAddToCache();
},
child: const Text("Set Cache"),
),
ElevatedButton(
onPressed: () async {
_onClearCache();
},
child: const Text("Clear Cache"),
),
ElevatedButton(
onPressed: () async {
_onListCookies();
},
child: const Text("Save Cookies"),
),
ElevatedButton(
onPressed: () async {
_onSetCookie();
},
child: const Text("Set Cookies"),
),
ElevatedButton(
onPressed: () async {
_onClearCookies();
},
child: const Text("Clear Cookies"),
)
],
),
),
);
}
Future<void> _onListCookies() async {
final String cookies = await controller
.runJavaScriptReturningResult('document.cookie') as String;
FlutterSecureStorage secureStorage = const FlutterSecureStorage();
secureStorage.write(key: 'cookies', value: cookies);
}
Future<void> _onSetCookie() async {
FlutterSecureStorage secureStorage = const FlutterSecureStorage();
String? cookies = await secureStorage.read(key: 'cookies');
/// get cookies from flutter secure storage and set them and refresh the page with new cookies.
/// please fill the required fields.
await cookieManager.setCookie(
WebViewCookie(
name: '',
/// required you have to set this
value: cookies!,
domain: '',
/// required
path: '/',
/// required
),
);
/// this will load the new page
await controller.loadRequest(Uri.parse(
'',
/// <---- refresh url
));
}
Future<void> _onClearCookies() async {
final bool hadCookies = await cookieManager.clearCookies();
String message = 'There were cookies. Now, they are gone!';
if (!hadCookies) {
message = 'There are no cookies.';
}
print(">>>>>>>>>> message $message");
}
Future<void> _onAddToCache() async {
/// <--- you have to write the logic to add cache and local storage from flutter secure storage. like this and refresh the page.
await controller.runJavaScript(
'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";',
);
}
Future _onListCache() async {
await controller.runJavaScriptReturningResult('caches.keys()');
/// <--- get cache and local storage and save it in flutter secure storage.
}
Future<void> _onClearCache() async {
await controller.clearCache();
await controller.clearLocalStorage();
}
}
I have a very tricky situation, which I've reproduced in a demo.
I have a Provider of a user, with this method of updating the listeners:
class User extends ChangeNotifier {
...
User({required this.uid}) {
Database().getUser(uid).listen(
(user) async {
displayName = user?.displayName;
email = user?.email;
phoneNumber = user?.phoneNumber;
photoURL = user?.photoURL;
did = user?.did;
interests = user?.interests;
notifyListeners();
},
onError: (e) => print(e),
);
}
...
}
My main.dart starts like this:
return MultiProvider(
providers: [
ChangeNotifierProvider<AuthState>.value(value: _authState),
ChangeNotifierProvider<ThemeModel>(create: (_) => ThemeModel())
],
child: Consumer<AuthState>(
builder: (context, auth, child) {
var user =
auth.authUser == null ? null : User(uid: auth.authUser!.uid);
return MultiProvider(
providers: [
ChangeNotifierProvider<ZUser?>.value(
value: zuser,
),
],
child: MaterialApp.router(...
This has been sufficient for my use case thus far.
Now, I wish to make an update to the interests field;
I have a DB widget that does:
Future updateUser(String uid, Map<String, Object?> data) async {
return userCollection.doc(uid).update(data);
}
Where the userCollection is my collection in Firestore.
I call this class from my view widget, as:
ZWideButton(
text: "Save",
onPressed: () async {
setState(() {
_localEdit = false;
_loading = true;
});
await user.saveInterests(_interests());
setState(() => _loading = false);
},
),
Where saveInterests is:
Future saveInterests(List<String> interests) async {
return _db.updateUser(uid, {"interests": interests});
}
None of this presents any problem at first -- I can update the interests and it works fine. That is, until I keep updating the interests, and it gets slower and slower each time (the browser says the download time gets longer and longer) and seemingly my computer is eating up more and more memory until the webpage ultimately crashes.
Something of a memory leak appears to be happening, but I'm unsure what about flutter web and firebase could be causing it. I believe it may have to do with the Provider package not disposing appropriately. It does not seem to be the provider as I don't see the Widget being rebuilt over and over. Looking for some thoughts.
For anyone looking; My issue is that my json deserializer was causing an infinite loop with the firebase listener
I have set up a Textformfield where users can fill in their shop & Geopoint coordinates.
They then press the submit button and it updates on our Firestore collection.
The problem I am having is how to save it as Geopoint's and not a string.
SizedBox(child: ElevatedButton(
onPressed: () async {
await collectionReference.add({
'shopName': _textEditingController.text,
'address': _textEditingController2.text,
'description': _textEditingController3.text,
'thumbNail': _textEditingController4.text,
'locationCoords':_textEditingController5.GeoPoint
},);
},
chil:Text('Add Data'),
))
Geopoint there is GeoPoint object inside Firestore plugin
'locationCoords':GeoPoint(_textEditingController5.GeoPoint.latitude,_textEditingController5.GeoPoint.longitude);
I am trying to read data from firebase inside an AlertDialog in flutter, when a button is pressed, and then update it afterwards.
I have tried using a StreamBuilder, but nothing happens
new FlatButton(
child: const Text('+ Add'),
onPressed: () {
StreamBuilder(
stream: Firestore.instance.collection('users').document(user.uid).collection('Filtre').document('ChooseSelf').snapshots(),
builder: (context, snapshot) {
var TypeSelfFilters = snapshot.data;
List<String> ListOfTypeSelf = List.from(TypeSelfFilters["Personer"]);
ListOfTypeSelf.add("value of TextFormField");
Firestore.instance.collection('users').document(user.uid).collection('Filtre').document('ChooseSelf').updateData({'Personer': ListOfTypeSelf});
}
);
Navigator.pop(context);
}
);
I do not get any errors, but the code inside the StreamBuilder is not executed for some reason.
Thank You
Hm... It looks to me that you are expecting to get the data when the use taps on FlatButton.
Let's look what happens:
tap on FlatButton
Instantiate a StreamBuilder
Start getting data from Firestore
Do some Firestore magic, update date
Then close dialog by navigator.pop()
Problem: you call navigator.pop() right after Instantiation of StreamBuilder. StreamBuilder has to wait somewhat to get the data. If you pop a route, and with that destroying your alert dialog, the builder callback will not be called. So the actual sequence of things happening is: Tap -> Instantiate StreamBuilder -> pop route
Recommendation: why wrap your computation in a StreamBuilder? You could do:
onPressed: () {
Firestore.instance.collection('users')/*...*/.snapshots().then((snapshot) async {
// then branch is executed once snapshot is retrieved from firestore
var TypeSelfFilters = snapshot.data;
// do some more computation and magic
await Firestore.instance.collection/*...*/.updateData();
// wait for updateData to finish
Navigator.pop(context); // this context is not the context inside the StreamBuilder
});
}
Thanks to Daniel V. i found a solution:
var myData = Firestore.instance.collection('users').document(user.uid).collection('Filtre').document('ChooseSelf').snapshots().first;
myData.then((snapshot) async {
var TypeSelfFilters = snapshot.data;
List<String> ListOfTypeSelf = List.from(TypeSelfFilters["Personer"]);
ListOfTypeSelf.add("bare en test");
Firestore.instance.collection('users').document(user.uid).collection('Filtre').document('ChooseSelf').updateData({'Personer': ListOfTypeSelf});
Navigator.pop(context); // this context is not the context inside the StreamBuilder
});
}
)
I have a list view with limited data and a full dialog box with full details which a user can change basically make a book request to network.As network call becomes asyn which results in pop out the dialog box earlier than changed data.
I am using Bloc Pattern and await for the network call.
new FlatButton(
child: new Text('BOOK', style: theme.textTheme.body1.copyWith(color: Colors.white)),
onPressed: () {
openShiftBloc.bookPressedSink.add(event.id);
Timer(Duration(milliseconds: 500), () => Navigator.pop(context, DismissDialogAction.cancel));
// Navigator.pop(context, DismissDialogAction.save);
}
)