I cannot get the data from the firestore into my application - flutter

I am new to programming and while i was trying to create a small TODO app, i was able to save the data into cloud firestore but the problems ocurred when i tried to retrieve the data using Streambuilder. I was following old tutorials before null safety , so i suspect that issues are regarding the null safety.
The code works without any errors in android studio but the data from firestore can't be retrieved.
The code is as follows:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class AddTODOlist extends StatelessWidget {
static String id = 'add_todolist';
final TextEditingController _controller = TextEditingController();
void _addUser(){
FirebaseFirestore.instance.collection("Todos").add({"title" : _controller.text});
_controller.text = "";
}
Widget _buildList(QuerySnapshot? snapshot){
return ListView.builder(
itemCount: snapshot!.docs.length,
itemBuilder: (context, index){
final doc = snapshot.docs[index];
final map = (doc.data()as dynamic)['title'];
return ListTile(
title: Text(map,style: TextStyle(color: Colors.black),),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Loan System',),
centerTitle: true,
),
body: SafeArea(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(
hintText: 'Add new user',
),
),
),
TextButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(Colors.blue),
),
onPressed:(){
_addUser();
},
child: Text('Add',
style: TextStyle(
color: Colors.white,
),),
),
],
),
StreamBuilder<QuerySnapshot?>(
stream: FirebaseFirestore.instance.collection('todos').snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData) return LinearProgressIndicator();
else {
return Expanded(
child: _buildList(snapshot.data),
);
}
}
),
],
),
),
),
);
}
}

You have an error in the spelling of the collection name. Remember that Firestore is case sensitive
Todos vs todos
FirebaseFirestore.instance.collection("Todos").add({"title" : _controller.text});
and here:
stream: FirebaseFirestore.instance.collection('todos').snapshots(),
Let me know if this does not help

Related

Flutter List View Builder Using API

I am able to fetch data from API and I can show it in the List View, but i want to show the first card as the latest updated data.
I tried to use the Reverse property of List View Builder, it some how solve my problem but gave rise to another problem. Now i need to scroll upward in order to see latest updated data.
Help me through it
I just want to show the latest updated data at the top and then scroll down to see another data.
Please Help Me.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class PromotersDetails extends StatefulWidget {
final String url,title;
PromotersDetails({Key key, #required this.url, #required this.title}) : super(key: key);
#override
_PromotersDetailsState createState() => _PromotersDetailsState(url,title);
}
class _PromotersDetailsState extends State<PromotersDetails> {
fetchSelfies() async {
var url1;
url1 = await http.get(Uri.parse(
url));
var res = json.decode(url1.body);
print(res);
return json.decode(url1.body)['selfies'];
}
String url,title;
_PromotersDetailsState(this.url, this.title);
#override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width * 0.6;
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
centerTitle: false,
title: Text(
title,
style: TextStyle(fontSize: 25.0, color: Colors.white),
),
elevation: 0.0,
backgroundColor: Colors.green,
),
body: FutureBuilder(
future: fetchSelfies(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
}
if (snapshot.hasData) {
return ListView.builder( **// List View Builder**
reverse: false,
cacheExtent: 10000.00,
itemCount: snapshot.data.length,
padding: EdgeInsets.all(8),
itemBuilder: (BuildContext context, int index) {
return Row(
children: [
Container(
height: 120,
alignment: Alignment.center,
child: Container(
height: 120,
width: 120,
child: Card(
child: Image.network(snapshot.data[index]['image']),
),
),
),
Expanded(
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 10,
),
Row(
children: [
Text(
"Time: ",
style: TextStyle(color: Colors.black),
),
Text(
snapshot.data[index]['time'],
style: TextStyle(color: Color(0xff868597)),
),
],
),
Container(
height: 50,
child: Text(
snapshot.data[index]['location'],
style: TextStyle(color: Color(0xff868597)),
),
),
],
),
),
),
],
);
},
);
}
return Center(
child: CircularProgressIndicator(),
);
})
);
}
}
If you just need to sort the API response backwards, you can use the reverse property of ListView.
ListView.builder(
reverse: true,
...
),
If the way you're populating the ListView is by appending new data, then it's better to store the data on a List then do sort/reversed. Though the sorting should be best done at the backend instead of passing the burden to the client app.

Flutter include content into a page

I actually have a searchBar(autocomplete) that is working.
When i select a result, the displaySnack() is working, it displays a snackBar, but i would like to display the content of testList().
My goal is to understand how I can launch another widget, to be able to add new widget on the page again and again.
My final goal is once i have the selected value, to make an http request, get a list as return and display a listview.
The function is executed ( i can see it in debugger ) but doesn't display anything..
(i'm new to flutter, so please explain your response if possible :) )
onSuggestionSelected : yes i know that it is void..
import 'package:drawer/src/share/snack_bar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import '../models/post_model.dart';
import '../services/http_service.dart';
// import 'package:http/http.dart' as http;
class PostsPage extends StatelessWidget {
final String title;
const PostsPage({Key? key, required this.title}) : super(key: key);
static Future<List<Post>> filterList(String value) async {
List<Post> list = await HttpService.fetchPosts();
return list.where(
(x) => x.title.toLowerCase().contains(value.toLowerCase())).toList();
}
#override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(title),
),
body: SafeArea(
child: Container(
padding: EdgeInsets.all(16),
child: TypeAheadField<Post?>(
debounceDuration: Duration(milliseconds: 500),
hideSuggestionsOnKeyboardHide: false,
textFieldConfiguration: TextFieldConfiguration(
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
hintText: 'Select the namespace...',
),
),
suggestionsCallback: filterList,
itemBuilder: (context, Post? suggestion) {
final user = suggestion!;
return ListTile(
title: Text(user.title),
);
},
noItemsFoundBuilder: (context) => Container(
height: 100,
child: Center(
child: Text(
'No Namespace Found.',
style: TextStyle(fontSize: 24),
),
),
),
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
testList(context); ################################ HERE
},
),
),
),
);
}
Widget testList(BuildContext context) {
return ListView.separated(
separatorBuilder: (BuildContext context, int index) => const Divider(),
itemCount: 2,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text("ppp"),
subtitle: Text("ppp"),
leading: CircleAvatar(
backgroundImage: NetworkImage(
"https://images.unsplash.com/photo-1547721064-da6cfb341d50"))
));
});
}
I need that : https://prnt.sc/136njev
It is obvious that you want the widget to rebuild to show the result. The most straightforward method is to use StatefulWidget. So I use it in your case(You can also find lots of ways to manage the state List of state management approaches)
Change your PostsPage to a StatefulWidget and rebuild when the user is selected
Add a Column in your PostsPage and separate into 2 parts: TypeAheadField & Result
Result part can use FutureBuilder (which can show loading indicator when data is not ready)
PostsPage:
class PostsPage extends StatefulWidget {
final String title;
const PostsPage({Key? key, required this.title}) : super(key: key);
static Future<List<Post>> filterList(String value) async {
// skip
}
#override
_PostsPageState createState() => _PostsPageState();
}
class _PostsPageState extends State<PostsPage> {
Post? user;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: SafeArea(
child: Column(
children: [
Container(
padding: EdgeInsets.all(16),
child: TypeAheadField<Post>(
// ...
// skip
// ...
onSuggestionSelected: (Post? suggestion) {
setState(() {
user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
});
},
),
),
Expanded(child: MyResult(post: user)),
],
),
),
);
}
}
Result part:
(I make it an isolated StatelessWidget just for better reading. You can use the original method to build the widget)
class MyResult extends StatelessWidget {
const MyResult({
required this.post,
Key? key,
}) : super(key: key);
final Post? post;
Future<List<OtherObject>> getOtherObjects(Post? post) async{
if(post == null){
return [];
}else{
return Future.delayed(Duration(seconds:3),()=>[OtherObject(title: '001'),OtherObject(title: '002'),OtherObject(title: '003')]);
}
}
#override
Widget build(BuildContext context) {
return FutureBuilder<List<OtherObject>>(
future: getOtherObjects(post),
builder: (context, snapshot) {
if(snapshot.hasData && snapshot.connectionState == ConnectionState.done) {
final result = snapshot.data!;
return ListView.separated(
separatorBuilder: (BuildContext context,
int index) => const Divider(),
itemCount: result.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
title: Text(result[index].title),
subtitle: Text("ppp"),
leading: CircleAvatar(
backgroundImage: NetworkImage(
"https://images.unsplash.com/photo-1547721064-da6cfb341d50"),
),
),
);
},
);
}else {
return Center(child: CircularProgressIndicator());
}
}
);
}
}
So what you are doing is basically just creating a ListView with your testList() function call and doing nothing with it, but what you want to do is to have that widget show up on the screen, right?
Flutter doesn't just show Widget if you create a new one, you must tell it to render. Just imagine you are doing preparing Widgets (e.g. Widgets in Widgets) and Flutter would render it immediately to the screen without you being finished, that wouldn't be that great.
You need to push that Widget over the Navigator widget that Flutter provides you.
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
Navigator.push(
context,
MaterialPageRoute(builder: (context) => testList(context)),
);
}
I suggest you to read this article to Navigation Basics.
you can use listView builder to show the selected results.
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
//get results
var results = fetchResult(suggestion);
//return a listview of the results
return ListView.builder(
physics: const AlwaysScrollableScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.vertical,
itemCount: results.length,
itemBuilder: (_context, index) {
Post post = results[index];
return Card(
elevation: 2,
child: InkWell(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
border: Border.all(color: Colors.black),
),
child: DefaultTextStyle(
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 12,
color: Colors.white),
child: Row(children: [
Expanded(
flex: 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(post.data),
],
),
),
]),
),
),
onTap: () {
//do something when user clicks on a result
},
));
}
},
If you want to show a list of selected items then you will have to add a ListView in the widget tree. Also use a StatefullWidget instead of StatelessWidget, because whenever you select an item, the selected list gets changed thus state.
sample code for state
List<Post> selectedPosts;
#override
void initState() {
super.initState();
selectedPosts = [];
}
#override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: Text(title),
),
body: SafeArea(
child: Column(
children: [
Container(
padding: EdgeInsets.all(16),
child: TypeAheadField<Post?>(
debounceDuration: Duration(milliseconds: 500),
hideSuggestionsOnKeyboardHide: false,
textFieldConfiguration: TextFieldConfiguration(
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
hintText: 'Select the namespace...',
),
),
suggestionsCallback: filterList,
itemBuilder: (context, Post? suggestion) {
final user = suggestion!;
return ListTile(
title: Text(user.title),
);
},
noItemsFoundBuilder: (context) => Container(
height: 100,
child: Center(
child: Text(
'No Namespace Found.',
style: TextStyle(fontSize: 24),
),
),
),
onSuggestionSelected: (Post? suggestion) {
final user = suggestion!;
displaySnack(context, ' Namespace: '+user.title);
setState(()=> selectedPosts.add(suggestion));
},
),
),
testList(context),
],
),
),
);
and in the testList function change the itemcount
itemCount: 2,
to
itemCount: selectedPosts?.length ?? 0,

Data From multiple FutureBuilders in flutter

I'm fetching data from an api source , the data is fetched properly , then i store the data in sqflite , so basically after doing both , i need to check if there is connection so that i show data from internet other than that i get data back from database , now since i'm using futurebuilder which return internet async operation result , how would i be also to get list of data from database , any help is appreciated guys and thank you in advance.
This is what i have tried so far
#override
void initState() {
super.initState();
dbHelper = DbHelper();
}
#override
Widget build(BuildContext context) {
return Scaffold (
appBar: AppBar(
title: Text("News Application"),
centerTitle: true,
backgroundColor: Colors.black,
titleTextStyle: TextStyle(color: Colors.white),
),
body: FutureBuilder (
future: Future.wait([getEverything(),dbHelper.getAllNews()]),
builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) {
if(snapshot.hasError) {
// So basically here if there is an error , i woul like to show data from database
// i tried to get data from snapshot like this : snapshot.data[0]...and snapshot.data[1]
// but no data is returned..
return new Center(
child: new CircularProgressIndicator(
backgroundColor: Colors.black,
),
);
} else {
if(snapshot.connectionState == ConnectionState.done){
return new Container(
color: Colors.black,
child: GridView.count(
padding: const EdgeInsets.all(20),
crossAxisCount: 2,
children: List.generate(snapshot.data.articles.length, (index) {
return new GestureDetector(
onTap: (){
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DetailsScreen(
image: snapshot.data.articles[index].urlToImage,
author: snapshot.data.articles[index].author,
title: snapshot.data.articles[index].title,
description: snapshot.data.articles[index].description,
publishedAt: snapshot.data.articles[index].publishedAt,
content: snapshot.data.articles[index].content,
))
);
},
child: Card(
elevation: 12,
child: new Column(
children: [
Image.network(snapshot.data.articles[index].urlToImage,
width: 250,),
Text(snapshot.data.articles[index].description)
],
),
),
);
}
)));
}
}
return new Center(
child: Visibility(
visible: true,
child: CircularProgressIndicator(
backgroundColor: Colors.black,
),
),
);
},
),
);
}

How to save downloaded data from an API to RAM in Flutter?

I'm writing a really simple app in Flutter, but I have a problem with state management.
Here's the video of what I have. Link: https://streamable.com/ir3ztr
The video shows my application, but when I switch a screen using Bottom Navigation Bar, the data loads again and again from the API. I don't want that. I want the once downloaded data to be saved in RAM and not being downloaded again from the API. Is that possible? I heard about Provider, but I don't know how to use that in my case.
Is there anyone who can help me?
My code:
World
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_placeholder_textlines/flutter_placeholder_textlines.dart';
import '../../models/world.dart';
import '../../data/world_service.dart';
class WorldScreenAndroid extends StatefulWidget {
#override
_WorldScreenAndroidState createState() => _WorldScreenAndroidState();
}
class _WorldScreenAndroidState extends State<WorldScreenAndroid> {
Future<World> futureWorld;
#override
void initState() {
super.initState();
futureWorld = fetchWorld();
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8.0),
child: FutureBuilder<World> (
future: futureWorld,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView(
children: [
Card(
child: ListTile(
leading: Icon(Icons.public),
title: Text('coronavirus_cases').tr(context: context),
subtitle: Text(NumberFormat('#,###,###', 'en_US').format(snapshot.data.cases).toString())
),
),
Card(
child: ListTile(
leading: Icon(Icons.public),
title: Text('deaths').tr(context: context),
subtitle: Text(NumberFormat('#,###,###', 'en_US').format(snapshot.data.deaths).toString())
),
),
Card(
child: ListTile(
leading: Icon(Icons.public),
title: Text('recovered').tr(context: context),
subtitle: Text(NumberFormat('#,###,###', 'en_US').format(snapshot.data.recovered).toString())
),
)
],
);
}
return ListView(
children: [
Card(
child: ListTile(
leading: Icon(Icons.public),
title: Text('coronavirus_cases').tr(context: context),
subtitle: PlaceholderLines(
count: 1,
animate: true,
color: Colors.grey,
minWidth: 0.10,
maxWidth: 0.50,
),
),
),
Card(
child: ListTile(
leading: Icon(Icons.public),
title: Text('deaths').tr(context: context),
subtitle: PlaceholderLines(
count: 1,
animate: true,
color: Colors.grey,
minWidth: 0.10,
maxWidth: 0.50,
),
),
),
Card(
child: ListTile(
leading: Icon(Icons.public),
title: Text('recovered').tr(context: context),
subtitle: PlaceholderLines(
count: 1,
animate: true,
color: Colors.grey,
minWidth: 0.10,
maxWidth: 0.50,
),
),
)
],
);
},
)
);
}
}
Countries
import 'package:flutter/material.dart';
import 'package:easy_localization/easy_localization.dart';
import '../../models/country.dart';
import '../../data/countries_service.dart';
class CountriesScreenAndroid extends StatefulWidget {
#override
_CountriesScreenAndroidState createState() => _CountriesScreenAndroidState();
}
class _CountriesScreenAndroidState extends State<CountriesScreenAndroid> {
Future<List<Country>> futureCountries;
#override
void initState() {
super.initState();
futureCountries = fetchCountries();
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(8.0),
child: FutureBuilder(
future: futureCountries,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: [
TextField(),
SizedBox(height: 10.0),
Expanded(
child: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
final List<String> _countriesAllArgs = [
NumberFormat('#,###,###', 'en_US').format(snapshot.data[index].cases),
NumberFormat('#,###,###', 'en_US').format(snapshot.data[index].todayCases),
NumberFormat('#,###,###', 'en_US').format(snapshot.data[index].active),
NumberFormat('#,###,###', 'en_US').format(snapshot.data[index].deaths),
NumberFormat('#,###,###', 'en_US').format(snapshot.data[index].todayDeaths),
NumberFormat('#,###,###', 'en_US').format(snapshot.data[index].recovered),
NumberFormat('#,###,###', 'en_US').format(snapshot.data[index].critical)
];
return Card(
child: Padding(
padding: EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
snapshot.data[index].country,
style: TextStyle(
fontSize: 18.0,
color: Colors.grey[600],
),
),
SizedBox(height: 6.0),
Text(
'countries_all',
style: TextStyle(
fontSize: 14.0,
color: Colors.grey[800],
),
).tr(args: _countriesAllArgs),
],
),
)
);
}
),
)
],
);
}
return Center(
child: CircularProgressIndicator(),
);
},
)
);
}
}
Make sure to keep the loaded data in a parent widget, not in the widget your switching.
Have you considered using the BLOC framework?
In that case, you could nicely sepearte Business Logic from UI code and keep the loaded data in the bloc.
In detail have a blocProvider on top, wrapping your app.
https://bloclibrary.dev/#/flutterbloccoreconcepts?id=blocprovider
Alternatively, but be carefull with the handling though, you can just create an object, anywhere outside of any widget, and access it from anywhere.
class Data {
List<String> stringsLoadedFromWeb;
}
Data data = Data();
// from anywhere else, where you import the above file
data.stringsLoadedFromWeb = ...

Flutter Cant Call FireStore Document Variables in same Class

I've been playing around trying to learn flutter and programming in general. I have a problem here that I have not been able to find a solution for on the forums
or anywhere else. I want to display the details of a FireStore Document on this view. The previous view is List view from Flutter only showing the Title.
The previous view(main) passes the document ID into this view as "partID." I have been able to successfully query that document for a snapshot and even print
out particular details. However, when I try to add the variables as the "existing text" in a form field, it doesn't recognize them.
What am I missing?
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'ItemData.dart';
class View extends StatefulWidget {
final String partID;
View({Key key, this.partID}): super (key: key);
#override
ViewState createState() => ViewState();
}
class ViewState extends State<View> {
Data newData = new Data();
#override
Widget build(BuildContext context) {
getItem();
return new Scaffold(
//CreateWidget()
appBar: AppBar(
backgroundColor: Colors.black,
title: Text("Item Data"),
),
body:
new Column(
children: <Widget>[
Flexible(
flex: 0,
child: Center(
child: Form(
//key: this._formKey,
child: Flex(
direction: Axis.vertical,
children: <Widget>[
ListTile(
title: TextFormField(
initialValue: newData.title,
decoration: new InputDecoration(
icon: new Icon(Icons.edit),
),
)
),
ListTile(
title: TextFormField(
initialValue: title,
decoration: new InputDecoration(
icon: new Icon(Icons.edit),
),
)
),
new Text("${widget.partID}"),
],
),
),
),
),
],
)
);
}
Future getItem() async {
DocumentSnapshot snapshot = await Firestore.instance.collection('items').document('${widget.partID}').get();
String title = snapshot['title'];
String location = snapshot.data['location'].toString();
print('${title}');
print('${location}');
}
}
For this you should be using something like a future builder as your body. It still allows loading asynchronously and you can also show a loading indicator. It also allows you to access all your values from the database within the child widgets.
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text(title)),
body: new FutureBuilder(
future: Firestore.instance.collection('items').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return const Text('Loading...');
return new Column(
children: <Widget>[
Flexible(
flex: 0,
child: Center(
child: Form(
//key: this._formKey,
child: Flex(
direction: Axis.vertical,
children: <Widget>[
ListTile(
title: TextFormField(
initialValue: snapshot.data["title"],
decoration: new InputDecoration(
icon: new Icon(Icons.edit),
),
)),
ListTile(
title: TextFormField(
initialValue: title,
decoration: new InputDecoration(
icon: new Icon(Icons.edit),
),
)),
new Text("${widget.partID}"),
],
),
),
),
),
],
);
}),
);
}
This is an example of how your code would be laid out when using a FutureBuilder.