I am new at Flutter and I try to build an App. I want to Display an double on a Textwidget from Cloud Firestore.
My CloudFirestore look like this:
How can I read the double of 200 in a TextWidget and Displays all the time the new value?
I have create a function to add Integer on the specific logged In User Mail what looks like this:
geldNew = _firestore.collection('guthaben').doc(loggedInUser?.email).set({
'geld': geld,
});
You can use a StreamBuilder and the snapshots to stream the value, so when there's a change, the UI gets updated automatically like so:
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('guthaben')
.doc("loggedInUser?.email")
.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return const CircularProgressIndicator();
}
return Text((snapshot.data!.data() as Map)["geld"].toString());
},
),
),
);
}
}
If you want to get the value just once, you can use a FutureBuilder like so:
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FutureBuilder<DocumentSnapshot>(
future: FirebaseFirestore.instance
.collection('guthaben')
.doc("loggedInUser?.email")
.get(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return const CircularProgressIndicator();
}
return Text((snapshot.data!.data() as Map)["geld"].toString());
},
),
),
);
}
}
Related
So, all I'm trying to do is create a StreamBuilder that listens to the "raids" collection on Firebase, and return a widget for each document using a ListView.builder (though I'm not entirely sure this is the right way to go about this, I'm pretty new).
From everything I've seen, my code should be working properly but obviously I've misunderstood something along the way.
I've already confirmed that the field I'm trying to pass into my Text widget is accurate and that there is data within the snapshots, what do I do next?
class HostedRaids extends StatefulWidget {
const HostedRaids({Key? key}) : super(key: key);
#override
State<HostedRaids> createState() => _HostedRaidsState();
}
class _HostedRaidsState extends State<HostedRaids> {
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: (FirebaseFirestore.instance.collection('raids').snapshots()),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (BuildContext context, int index) {
var raidSnapshot = snapshot.data!.docs[index];
return Row(
children: [
Text(
raidSnapshot['creatorID'],
style: const TextStyle(color: Colors.white),
),
],
);
},
);
} else {
throw ('error');
}
});
}
}
So, I need to retrieve the name of the sender of the message, not the phone number, could that be achieved with telephony? I only found message.address and no message.name, I need it for listening to incoming sms.
The reason for that is because I have a list of contacts that need to be matched with the information from the received message .
Thanks for your time!
sms: ^0.2.4
import 'package:flutter/material.dart';
import 'package:sms/sms.dart';
class ContactPage extends StatefulWidget {
const ContactPage({Key? key}) : super(key: key);
#override
State<ContactPage> createState() => _ContactPageState();
}
class _ContactPageState extends State<ContactPage> {
Future<List<SmsMessage>> getAllMessages() async {
SmsQuery smsQuery = SmsQuery();
return await smsQuery.getAllSms;
}
#override
Widget build(BuildContext context) {
return Material(
child: SafeArea(
child: Scaffold(
body: FutureBuilder<List<SmsMessage>>(
future: getAllMessages(),
builder: (context, snapshot){
if(snapshot.hasData){
return ListView.builder(itemBuilder: (context,index){
return Text(snapshot.data![index].sender);
},itemCount: snapshot.data!.length);
}
return Text("No sms");
},
),
),
),
);
}
}
I have a provider with an int variable currentPage that defines the initial page of a PageView. I have this because I want to change the currentPage with widgets that far under the tree, or descendent widgets. I've set up everything correctly, but when changeNotifier is called, the page doesn't change.
Here's the provider class-
class CurrentPageProvider with ChangeNotifier{
int? currentPage;
CurrentPageProvider({this.currentPage});
changeCurrentPage(int page) {
currentPage = page;
notifyListeners();
}
}
To use it, I've wrapped my MaterialWidget with a MultiProvider as such-
class Test extends StatelessWidget {
const Test({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => CurrentPageProvider(currentPage: 0))
],
child: MaterialApp(
title: "Test",
debugShowCheckedModeBanner: false,
theme: ThemeData.light().copyWith(
primaryColor: yellowColor,
),
home: const ResponsiveRoot(),
),
);
}
}
And here's the widget where the child should rebuild, but isn't-
class ResponsiveRoot extends StatelessWidget {
const ResponsiveRoot({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
int currentPage = Provider.of<CurrentPageProvider>(context).currentPage!;
print("CurrentPageUpdated");
return LayoutBuilder(
builder: ((context, constraints) {
if (constraints.maxWidth > kWebScreenWidth) {
return const WebscreenLayout();
} else { //The page view is here
return MobileScreenLayout(
currentPage: currentPage,
);
}
}),
);
}
}
Upon debugging, I've found out that "CurrentPageUdated" gets printed when I'm calling the changeCurrentPage. However, the initState of the MobileScreenLayout doesn't get called (This widget has the pageView)
How do I fix this? Thanks!
in order to update the state of the the app you need to use Consumer widget.
Consumer<Your_provider_class>(
builder: (BuildContext context, provider_instance, widget?){
},
child: any_widget, but not neccessary,
)
The problem seems to be that even though your Provider.of mechanism needs to listen to changes, it does not.
What you can do is, do the recommended way on the documentation and you can either use the watch extension function or use Consumer or Selector widgets.
Here is an example on how to do it with your example with a Selector.
For more information read about Selector here
class ResponsiveRoot extends StatelessWidget {
const ResponsiveRoot({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Selector<CurrentPageProvider, int>(
selector: (context, provider) => provider.currentPage!,
builder: (context, currentPage, child) {
print("CurrentPageUpdated");
return LayoutBuilder(
builder: ((context, constraints) {
if (constraints.maxWidth > kWebScreenWidth) {
return const WebscreenLayout();
} else {
//The page view is here
return MobileScreenLayout(
currentPage: currentPage,
);
}
}),
);
},
);
}
}
I've changed from Statefulwidget using initState to fetch the data and Futurebuilder to load it to Futureprovider. But it seems like Futureprovider is execute build method twice, while my previous approach executed it once. Is this behaviour normal?
class ReportsPage extends StatelessWidget {
const ReportsPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return FutureProvider<List<ReportModel>>(
create: (_) async => ReportsProvider().loadReportData(1),
initialData: null,
catchError: (_, __) => null,
child: const ReportWidg()
);
}
}
class ReportWidg extends StatelessWidget {
const ReportWidg();
#override
Widget build(BuildContext context) {
print("Execute Build");
final reportList = Provider.of<List<ReportModel>>(context);
if (reportList == null) {
return Center(child: CircularProgressIndicator());
} else if (reportList.isEmpty) {
return Center(child: Text("Det finns inga rapporter."));
}
print(reportList.length);
return Container();
}
}
Im relativly new to flutter but I think its because StatelessWidget is #immutable, which means whenever something changes it needs to rebuild itself.
At first build there is async calling made and ReportWidg() is rendered.
Then this line final reportList = Provider.of<List<ReportModel>>(context); get new fetched data as result of async function therefore immutable widget needs to rebuild itself because it cannot be "changed".
In object-oriented and functional programming, an immutable object
(unchangeable object) is an object whose state cannot be modified
after it is created. ... This is in contrast to a mutable object
(changeable object), which can be modified after it is created
or am I wrong ?
I suspect your FutureProvider should be hoisted to a single instantiation, like placed into a global variable outside any build() methods. This will of course cache the result, so you can set it up for rebuild by having the value depend on other Providers being watch()ed or via FutureProvider.family.
You can copy paste run full code below
Yes. it's normal
First time Execute Build reportList is null and show CircularProgressIndicator()
Second time Execute Build reportList has data and show data
If you set listen: false , final reportList = Provider.of<List<ReportModel>>(context, listen: false);
You get only one Execute Build and the screen will always show CircularProgressIndicator()
In working demo simulate 5 seconds network delay so you can see CircularProgressIndicator() then show ListView
You can reference https://codetober.com/flutter-provider-examples/
code snippet
Widget build(BuildContext context) {
print("Execute Build");
final reportList = Provider.of<List<ReportModel>>(context);
print("reportList ${reportList.toString()}");
if (reportList == null) {
print("reportList is null");
return Center(child: CircularProgressIndicator());
} else if (reportList.isEmpty) {
return Center(child: Text("Empty"));
}
return Scaffold(
body: ListView.builder(
itemCount: reportList.length,
working demo
full code
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ReportModel {
String title;
ReportModel({this.title});
}
class ReportsProvider with ChangeNotifier {
Future<List<ReportModel>> loadReportData(int no) async {
await Future.delayed(Duration(seconds: 5), () {});
return Future.value([
ReportModel(title: "1"),
ReportModel(title: "2"),
ReportModel(title: "3")
]);
}
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ReportsPage(),
);
}
}
class ReportsPage extends StatelessWidget {
const ReportsPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
return FutureProvider<List<ReportModel>>(
create: (_) async => ReportsProvider().loadReportData(1),
initialData: null,
catchError: (_, __) => null,
child: const ReportWidg());
}
}
class ReportWidg extends StatelessWidget {
const ReportWidg();
#override
Widget build(BuildContext context) {
print("Execute Build");
final reportList = Provider.of<List<ReportModel>>(context);
print("reportList ${reportList.toString()}");
if (reportList == null) {
print("reportList is null");
return Center(child: CircularProgressIndicator());
} else if (reportList.isEmpty) {
return Center(child: Text("Empty"));
}
return Scaffold(
body: ListView.builder(
itemCount: reportList.length,
itemBuilder: (context, index) {
return Card(
elevation: 6.0,
child: Padding(
padding: const EdgeInsets.only(
top: 6.0, bottom: 6.0, left: 8.0, right: 8.0),
child: Text(reportList[index].title.toString()),
));
}),
);
}
}
In your case you should use Consumer, i.e.
FutureProvider<List<ReportModel>(
create: (_) => ...,
child: Consumer<List<ReportModel>(
builder: (_, reportList, __) {
return reportList == null ?
CircularProgressIndicator() :
ReportWidg(reportList);
}
),
),
But in this case you must to refactor your ReportWidg.
I have a characterList class that has a final List <Character> character field;
How can I access the character from the SWMain class?
SWMain class:
class _SWMainState extends State<SWMain> {
Icon customIcon = Icon(Icons.search);
static Text titleText = Text("Star Wars API");
Widget customSearchBar = titleText;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar()
body: FutureBuilder<List<Character>>(
future: fetchCharacters(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? CharacterList(character: snapshot.data)
: Center(child: CircularProgressIndicator());
},
),
),
);
}
}
characterList class:
class CharacterList extends StatelessWidget {
final List<Character> character;
CharacterList({Key key, this.character}) : super(key: key);
...
}
CharacterList({Key key, this.character}) : super(key: key);
this is the constructor.
You are using a stateless widget so you can directly access via variable name which is character in your case.
if you are using a stateful widget so you have to access it via widget.character
Thanks.