I'm using Flutter. I need to get just one value from Firestore and update it for all users. So I use a listen to keep the value updated. I receive the value in one variable but I can't use it outside the listen method.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class PaginaGraficos extends StatefulWidget {
#override
_PaginaGraficosState createState() => _PaginaGraficosState();
}
class _PaginaGraficosState extends State<PaginaGraficos> {
#override
Widget build(BuildContext context) {
String _totalGeradoApp = "0";
_getTotalGeradoApp () {
Firestore.instance.collection("dados_app").snapshots().listen(
( snapshot ){
var totalGeradoApp;
for( DocumentSnapshot item in snapshot.documents ){
var dados = item.data;
totalGeradoApp = dados["total_gerado_app"];
print("totalGeradoApp: $totalGeradoApp");
}
_totalGeradoApp = totalGeradoApp;
}
);
}
_getTotalGeradoApp();
print("_totalGeradoApp: $_totalGeradoApp");
return Container(
child: Text("$_totalGeradoApp"),
);
}
}
Some names are in Portuguese because I'm Brasilian but the code is still understandable.
I'm new with Dart so please tell me if I'm doing something stupid.
The function passed to the listen method will execute whenever the value is updated, but the rest of the code runs only once. So, if you want the the Text in the container to be updated whenever the value is updated - use a StreamBuilder.
#override
Widget build(BuildContext context) {
return Container(
child: StreamBuilder(
stream: Firestore.instance.collection("dados_app").snapshots(),
builder: (context, snapshot) {
if(!snapshot.hasData) {
return Text('Loading...');
}
// update _totalGeradoApp
var totalGeradoApp;
var docs = (snapshot.data as QuerySnapshot).documents;
for(var item in docs) {
var dados = item.data;
totalGeradoApp = dados["total_gerado_app"];
}
_totalGeradoApp = totalGeradoApp;
// return Text Widget with updated text
return Text("$_totalGeradoApp");
),
);
}
So, in your code the listener is added to the stream and immediately the next code is executed where a Container is created with _totalGeradoApp, which was "0" initially. Whenever the value was updated _totalGeradoApp is updated but the text in the Container is not. By using a StreamBuilder the Text widget is also updated whenever a new value is available.
Related
I have created a provider to pass down the values within the WidgetTree, but when I try to retrieve the value, it gives me null as its value.
Initializing the value for the Provider:
GetCurrentCourse course = GetCurrentCourse();
course.currentCourse(
courseID: courseID,
courseDocID: courseDocID,
);
Below is the code related to the Provider:
import 'package:flutter/material.dart';
class GetCurrentCourse extends ChangeNotifier {
String? currentCourseID;
String? currentCourseDocID;
void currentCourse({courseID, courseDocID}) {
this.currentCourseDocID = courseDocID;
this.currentCourseID = courseID;
print("Current Course ID is ${this.currentCourseID}");
print("Current Course Doc ID is ${this.currentCourseDocID}");
notifyListeners();
}
}
When I print the values within the above GetCurrentCourse class. I do see the correct values getting printed.
Below is the code showcasing the Provider defined within the main.dart file as shown below:
ChangeNotifierProvider<GetCurrentCourse>(
create: (_) => GetCurrentCourse(),
),
Below is the code where I'm trying to consume the GetCurrentCourse Provider:
class CoursePageBody extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("Printing Value of Course from Provider");
print(Provider.of<GetCurrentCourse>(context).currentCourseDocID);
.
.
}
}
Now, when I try to access the value of the Provider and try to print it. It prints null.
What can I try to fix it?
Your initialization code should be:
var courseProvider = Provider.of<GetCurrentCourse>(context, listen: false);
courseProvider.currentCourse(
courseID: courseID,
courseDocID: courseDocID,
);
And the widget that is going to access it should be wrapped in a Consumer that would look something like:
class CoursePageBody extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Consumer<GetCurrentCourse>(
builder: (context, courseProvider, child) {
print("Printing Value of Course from Provider");
print(courseProvider.currentCourseDocID);
// rest of the code
}
);
}
}
newbie to Flutter. My code runs but encounters a
The following LateError was thrown building
FutureBuilder(dirty, state:
_FutureBuilderState#e1a6f):
LateInitializationError: Field 'initialPosition' has not been
initialized.
The code is to set up a GoogleMap widget that takes initial position from the device. I get the red screen with that error, but after a few seconds the coordinates gets received and proceeds as normal and displays the map and position correctly.
Tried future as well but but I get other errors. Is it supposed to be under the FutureBuilder? In a wrapper.dart or my main.dart?
home.dart:
import 'package:flutter/material.dart';
import 'package:something/services/auth.dart';
import 'screens/map.dart';
import 'package:something/services/geolocator_service.dart';
class LakoApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<LakoApp> {
final AuthService _auth = AuthService();
final _geolocatorService = GeolocatorService();
late var initialPosition;
// #override
Future getInitialPosition <Position>() async {
initialPosition = await _geolocatorService.getInitialLocation();
return initialPosition;
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: FittedBox(
child: Text('Something something'),
),
actions: <Widget>[
// irrelevant code
// .....
],
body:
FutureBuilder(
future: getInitialPosition(),
builder: (context, snapshot) {
return Map(initialPosition);
}
)
);
}
}
Future Builders are built even before getting the data. So, you should check whether it has data.
if (snapshot.hasData) {
return Map(initialPosition); //Or snapshot.data.
}else{
return CircularProgressIndicator();
}
There are other problems here. I will show some further code to improve your own code.
Your method returns a Future of any type receiving a generic parameter called Position. I think you want to use a data type called position for that you need to move <Position> here as right now the way you are writing it is useless for your specific example.
Future<Position> getInitialPosition () async {
initialPosition = await _geolocatorService.getInitialLocation();
return initialPosition;
}
The FutureBuilder can be like this.
FutureBuilder<Position>(
future: getInitialPosition(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Map(snapshot.data);
}else{
return CircularProgressIndicator();
//Display loading, you may adapt this widget to your interface or use some state management solution
}
}
)
Edited the code according to suggestions: got rid of the method and variable, because its redundant
body: FutureBuilder <Position> (
future: _geolocatorService.getInitialLocation(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Map(snapshot.data!);
}else {
return Loading();
I am trying to show the price of items in the cart but the total value should be shown in TextField. I am saving data to SQLite and retrieving then show to a widget, but when I try to access total_price to another widget it's not updating, but When I press hot reload again the data shows but not first time when I am opening the page
return FutureBuilder<List<CartModel>>(
future: fetchCartFromDatabase(),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data.length > 0) {
cartCount = snapshot.data.length;
for(int i = 0;i<snapshot.data.length;i++){
var price = snapshot.data[i].product_price.split("₹");
total_price+=double.parse(price[1]);
}
} else if (snapshot.hasData && snapshot.data.length == 0) {
return new Text("No Data found");
}
else
{
return new Container(
alignment: AlignmentDirectional.center,
child: new CircularProgressIndicator(),
);
}
);
value initialized
int cartCount = 0;
double total_price=0.0;
The FutureBuilder updates only its children. To update the value of another widget you must use setState.
The best way would be putting FutureBuilder in an upper level or using some sort of state manager, like provider.
To use setState you need to initialize you fetch from an initState of a stetefullWidget (or to call it from a function). This way you will not need a FutureBuilder and must refactor your code:
class YourWidget extends StatefulWidget {
#override
_YourWidgetState createState() => _YourWidgetState();
}
class _YourWidgetState extends State<YourWidget> {
double total_price = 0;
#override
void initState() {
super.initState();
fetchCartFromDatabase().then((value){
setState((){
for(int i = 0;i<value.length;i++){
var price = value[i].product_price.split("₹");
total_price+=double.parse(price[1]);
}
});
});
}
}
The addPostFrameCallback is not a good solution, since it updates the value only in the next frame. When the app grows it leads to lags.
To continue using the FutureBuilder, move your widget tree that needs to be updated to be inside of the FutureBuilder.
I have a question about Flutter and Firestore.
I want to wait until another app set the bool from Firestore "roomStart" to true, to open a view. If "roomStart" is false, it should wait until it is set to true and then start the if statement again.
class QuizPage extends StatefulWidget {
final Room room;
QuizPage(this.questions, this.room);
#override
_QuizPageState createState() => _QuizPageState(room);
}
class _QuizPageState extends State<QuizPage> {
final Room room;
_QuizPageState(this.room);
#override
Widget build(BuildContext context) {
if(room.roomStart) {
return MaterialApp(
home: Scaffold(
//code
);
} else {
// code: wait for boolean is set on true
);
}
}
}
enter image description here
The idea i had was to set a setState but i still lack the right approach, does anyone have an example or a hint?
I would be very grateful.
Using the Firebase SDK you can get a Stream of data for your Document (Room) by calling onSnapshot() with firebase_cloudstore. The Stream will always output the latest value from firebase. You don't necessarily have to have a StatefulWidget, instead you can use a StatelessWidget with a StreamBuilder and you can put your if logic inside of the StreamBuilders builder method.
Make sure to check that the snapshot hasData and show the appropriate widget.
You can use streambuilder and listen snapshot as
import 'package:flutter/material.dart';
class App extends StatelessWidget {
#override
Widget build(context) {
return StreamBuilder(
stream: Firestore.instance
.collection('roomCollectionName')
.document('roomId')
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(themeColor),
),
);
} else {
if (snapshot.data['roomstart']) {
//true
return Container();
} else {
//false
return Container();
}
}
},
);
}
}
in flutter I use a class to load values for switch widgets from a database and then update that database when the switch is toggled. Somehow I need to have that class call setstate on the calling function of the instance but it doesn't seem to work.
See the code below for an example.
The first switch is how I'd write it without the database class. That is working fine, when tapping the switch it both moves and the print shows that the value changed.
In the second switch widget however, I used the database class to build it but it doesn't seem to call the callback function correctly. The print always prints false.
I thought I tried all combinations of => and (){} but something is still amiss. I'm pretty sure the problem is how the callback is handled in the line: callBackFunctionForSetState();
maybe that should be called with callBackFunctionForSetState((){}); but that also doesn't work.
import 'package:flutter/material.dart';
void main() {
runApp(App());
}
bool myBool = true;
class App extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Title',
home: ScreenUpgrades(),
);
}
}
class ScreenUpgrades extends StatefulWidget {
#override
_ScreenUpgradesState createState() => _ScreenUpgradesState();
}
class _ScreenUpgradesState extends State<ScreenUpgrades> {
#override
Widget build(BuildContext ctxt) {
return new Scaffold(
appBar: new AppBar(
title: new Text("Upgrades"),
),
body: FutureBuilder(
future: buildSwitchList(),
builder: (BuildContext ctxt, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView(children: snapshot.data);
} else {
return Center(child: CircularProgressIndicator());
}
}));
}
Future<List> buildSwitchList() async {
List<Widget> widgetList = [];
//This one below for a working example only
widgetList.add(Switch(value: myBool,onChanged: (bb)=>nonDBSetState()));
//Normally I'll create a bunch of widgets by loading their data from the DB as below
widgetList.add(DataBaseSwitchBuilder(1,()=>setState((){})).listViewWidget);
return widgetList;
}
nonDBSetState()
{
myBool = !myBool;
print('New value of first switch: ' + myBool.toString());
setState((){});
}
}
class DataBaseSwitchBuilder {
Widget listViewWidget;
int dbID;
bool onOff;
Function callBackFunctionForSetState;
DataBaseSwitchBuilder (int paramID, Function callBack)
{
dbID=paramID; //used to query the parameter from the DB
onOff = true;
callBackFunctionForSetState=callBack;
listViewWidget=(Switch(value: onOff,onChanged: (bb)=> updateDBAndState()));
}
updateDBAndState()
{
//update the switch
onOff = !onOff;
print('DB Swtich value now: ' + onOff.toString());
//first we save the record in the DB
//todo: code for updating DB
//Then call the passed function which should be a setstate from the calling function
//Below doesn't seem to work.
callBackFunctionForSetState();
}
}
I'm just expecting that the updateDBAndState will allow me to save the new value of the switch to the database and then call the setstate callback.
Just to respond to "How to pass setstate as a parameter to a class method"
widget controler
class Controller {
Controller._privateConstructor();
factory Controller() => _singleton;
static final Controller _singleton =
Controller._privateConstructor();
late Function setStateHandler;
void initSetState(Function setState) => setStateHandler = setState;
void triggerSetState() => setStateHandler();
}
widget
#override
void initState() {
super.initState();
controller.initSetState(() => setState(() {
widgetVariable = true;
}));
}