Hi, I want to make a list inside the Flutter Firebase field. I'm creating an id for followers in the Field. In Firebase, there is a collection, user ID and followers id in the field. My encodings are as follows. But I'm not making a list. What are the changes I will make?
Followers_card
class FollowersCard extends StatefulWidget {
final snap;
const FollowersCard({
Key? key,
required this.snap,
}) : super(key: key);
#override
State<FollowersCard> createState() => _FollowersCardState();
}
class _FollowersCardState extends State<FollowersCard> {
List<dynamic> followersList = []; // shouldn't use dynamic
getdata() async {
await FirebaseFirestore.instance
.collection("users")
.doc(FirebaseAuth.instance.currentUser!.uid)
.get()
.then((value) async {
// get followerIds
List<String> follwerIds = List.from(value.data()!['followers']);
// loop through all ids and get associated user object by userID/followerID
for (int i = 0; i < follwerIds.length; i++) {
var followerId = follwerIds[i];
var data = await FirebaseFirestore.instance
.collection("users")
.doc(followerId)
.get();
// push that data into followersList variable as we are going
// to use that in listViewBuilder
followersList.add(data);
}
setState(() {});
});
#override
void initState() {
super.initState();
getdata();
}
}
#override
Widget build(BuildContext context) {
// use the listView builder to render the list of followers card
return SingleChildScrollView(
physics: NeverScrollableScrollPhysics(),
child: ListView.builder(
shrinkWrap: true,
itemCount: followersList.length,
itemBuilder: (context, index) {
var followerItem = followersList[index];
print('photoUrl');
return _buildFollowersCard(
followerItem['photoUrl'], followerItem['username']);
}),
);
}
Widget _buildFollowersCard(String photoUrl, String username) {
return Container(
height: 70,
width: double.infinity,
color: mobileBackgroundColor,
child: Card(
child: Column(children: [
//Header
Container(
height: 40,
width: double.infinity,
padding: const EdgeInsets.symmetric(
vertical: 4,
horizontal: 16,
).copyWith(right: 0),
child: Row(
children: [
CircleAvatar(
radius: 16,
backgroundImage: NetworkImage(
photoUrl,
),
),
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 8),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
username,
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
),
],
),
)
]),
),
);
}
}
followers_screen
class FollowersScreen extends StatefulWidget {
const FollowersScreen({Key? key}) : super(key: key);
#override
State<FollowersScreen> createState() => _FollowersScreenState();
}
class _FollowersScreenState extends State<FollowersScreen> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: mobileBackgroundColor,
centerTitle: true,
title: Image.asset(
'Resim/logo.png',
height: 50,
),
),
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('users').snapshots(),
builder: (context,
AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) => FollowersCard(
snap: snapshot.data!.docs[index].data(),
),
);
},
),
);
}
}
The problem is that _FollowersScreenState.initState is in the wrong place. It's inside the function getdata that it is trying to call. The initState is never called. That's why there is no list being built.
Also, setState is the one that assigns State values. So first, populate a temporary list of followers and then assign it to the State one inside the setState callback.
Below is the fixed snippet code for _FollowersScreenState:
class _FollowersCardState extends State<FollowersCard> {
List<dynamic> followersList = []; // shouldn't use dynamic
getdata() async {
List<dynamic> followers = [];
final currentUserSnapshot = await FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.get();
// get followerIds
List<String> follwerIds =
List.from(currentUserSnapshot.data()!['followers']);
// loop through all ids and get associated user object by userID/followerID
for (int i = 0; i < follwerIds.length; i++) {
var followerId = follwerIds[i];
var data = await FirebaseFirestore.instance
.collection('users')
.doc(followerId)
.get();
// push that data into the temp list variable as we are going
// to use that in to setState
followers.add(data);
}
setState(() => followersList = followers);
}
#override
void initState() {
super.initState();
getdata();
}
...
Related
This is my code:
class MobileHomePage extends StatefulWidget {
const MobileHomePage({Key? key}) : super(key: key);
#override
State<MobileHomePage> createState() => _MobileHomePageState();
}
class _MobileHomePageState extends State<MobileHomePage> {
#override
void setState(fn) {
if (mounted) {
super.setState(fn);
}
}
int? second;
#override
void initState() {
second = int.parse(DateFormat.s().format(DateTime.now()));
Timer.periodic(const Duration(seconds: 1), (timer) => getCurrentTime());
super.initState();
}
void getCurrentTime() {
setState(() {
second = int.parse(DateFormat.s().format(DateTime.now()));
});
}
User user = FirebaseAuth.instance.currentUser!;
final Stream<QuerySnapshot> _mainSubjectStream = FirebaseFirestore.instance
.collection('systems')
.orderBy('system_name', descending: false)
.snapshots();
#override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;
final GlobalKey<ScaffoldState> _key = GlobalKey();
return Scaffold(
key: _key,
body: SafeArea(
bottom: false,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
flex: 1,
child: Container(
color: Colors.transparent,
child: StreamBuilder<QuerySnapshot>(
stream: _mainSubjectStream,
builder: (context, mainSubjectSnapshot) {
if (mainSubjectSnapshot.hasError) {
return const Center(child: Text('Error));
}
if (mainSubjectSnapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
var length = mainSubjectSnapshot.data!.docs.length;
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: length,
itemBuilder: (context, i) {
var output = mainSubjectSnapshot.data!.docs[i];
return Text(output['name']);
});
},
),
)),
const Spacer(flex: 3),
Expanded(
flex: 5,
child: DatePicker(
DateTime.now(),
initialSelectedDate: _date,
daysCount: 8,
locale: 'pt',
selectionColor: const Color.fromRGBO(67, 97, 238, 1),
selectedTextColor: Colors.white,
onDateChange: (date){
setState(() {
_date = date;
});
},
),
),
Expanded(
flex: 1,
child: Text(second.toString()
),
],
),
));
}
}
When I try to display the clock (as in second.toString()), the StreamBuilder is also updated, resulting in a new progress indicator each second. What is breaking my head is that I just copied the same strutted that I used in other code – which works fine. Did I do something wrong? Why is it influencing the other stream?
EDIT
Have just found out that the error is related to the key in the scaffold. When I deleted it, it worked fine. The thing is that I need I as I have a custom button to open the drawer. 1st question is how does it influences it. 2nd question is how to solve it?
I am trying to transfer my data from FireBase Collection to a map. Then, the map data will go into MultiSelectBottomSheetField.
Problems: I am getting "Instance of '_JsonQueryDocumentSnapshot'" instead of 'Home' for example.
I still not getting the list of context from collection into MultiSelectItem
Also, I have noticed that when I start the app, the MultiSelectionItem is empty. It is displaying "Instance of '_JsonQueryDocumentSnapshot'" when I display the main view a second time. I guess that a SetState is probably missing somewhere. But as I can not write set state in a constructor, I am puzzled.
This is driving me nuts as I do not find where the problem is coming from.
Many thanks for your help.
import 'package:flutter_swipe_action_cell/core/cell.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/foundation.dart';
import 'package:multi_select_flutter/bottom_sheet/multi_select_bottom_sheet_field.dart';
import 'package:multi_select_flutter/util/multi_select_item.dart';
String inboxTaskDisplayed='';
int nbRecord=0;
var taskSelectedID;
var taskDone;
//------TEST
class ContextExisting {
final int id;
final String name;
ContextExisting({
this.id,
this.name,
});
}
List<ContextExisting> _contexts = [];
List <ContextExisting>allMyContext=[];
TextEditingController passController = new TextEditingController();
//-----------------
var documentID;
var textController = TextEditingController();
var popUpTextController = TextEditingController();
//_-----------------
class Inbox_Test_For_Chip_Trial extends StatefulWidget {
final String screenSelected;
final String titlePage;
Inbox_Test_For_Chip_Trial(Key key, {#required this.screenSelected, #required this.titlePage,}) : super(key: key);
#override
_Inbox_Test_For_Chip_TrialState createState() => _Inbox_Test_For_Chip_TrialState(screenSelected, titlePage);
}
class _Inbox_Test_For_Chip_TrialState extends State<Inbox_Test_For_Chip_Trial> {
GlobalKey<FormState> _inboxFormKey = GlobalKey<FormState>();
String screenSelected;
String titlePage;
_Inbox_Test_For_Chip_TrialState(this.screenSelected, this.titlePage,);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: new AppBar(
title: new Text(titlePage + ' ('+nbRecord.toString()+')'),
actions: <Widget>[
],
),
backgroundColor: Colors.white,
body: Container(
height: 250,
child: Column(
//mainAxisAlignment: MainAxisAlignment.center,
children: [
//FOR CONTEXT
Flexible(child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('contexts')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
else {
var docs = snapshot.data.docs;
print('docs');
print (docs[2].id);
print(snapshot.data.docs.length);
int nbContext = snapshot.data.docs.length;
for (int i=0;i<nbContext; i++) {
_contexts.addAll([ContextExisting (id:i, name:snapshot.data.toString())]);
print(_contexts);
}
return Container(
height: MediaQuery.of(context).size.height * .78,
width: MediaQuery.of(context).size.width,
child: ListView(
children: snapshot.data.docs.map((document) {
return Wrap(
children: [Card(
child: SwipeActionCell(
key: ObjectKey(document['context_Name']),
trailingActions: <SwipeAction>[
],
child: ListTile(
trailing: IconButton(
icon: Icon(Icons.keyboard_arrow_right),
onPressed: () async {
taskSelectedID = FirebaseFirestore
.instance
.collection('Users')
.doc(
FirebaseAuth.instance.currentUser
.uid)
.collection('contexts')
.doc(document.id).toString();
}
),
leading: ConstrainedBox(
constraints: BoxConstraints(
minWidth: 30,
minHeight: 35,
maxWidth: 30,
maxHeight: 35,
),
//InkWell(child: Icon(Icons.check_box_outline_blank),
),
title: Text(
document['context_Name'],
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
),
),
]
);
}).toList(),
),
);
}
}),),
Column(children:[
TestWidgetContext(),
]), //MyHomePage())
],
),
),
//bottomNavigationBar: MyBottomAppBar(), //PersistentBottomNavBar(),
);
}
class TestWidgetContext extends StatefulWidget {
TestWidgetContext({Key key}) : super(key: key);
#override
_TestWidgetContextState createState() => _TestWidgetContextState();
}
class _TestWidgetContextState extends State<TestWidgetContext> {
List itemsContext;
List<ContextExisting> _selectedContext5 = [];
final _itemsContext = _contexts
.map((context) => MultiSelectItem<ContextExisting>(context, context.name))
.toList();
#override
void initState() {
_selectedContext5 = _contexts;
super.initState();
}
#override
Widget build(BuildContext context) {
return Column(
children: [
MultiSelectBottomSheetField(
buttonText: Text("Contexts"),
onConfirm: (val2) {
},
items: _itemsContext,
// initialValue:
// _itemsContext,
),
],
);
}
}
Since the code has a lot of errors, I am writing down my findings.
allMyContext is a list and if you wish to add elements to it you should be wring it as
allMyContext.add(ContextExisting (id:i, name:doc.name));
instead of List<allMyContext> = ContextExisting (id:i, name:doc.name)
Insde the on pressed
taskSelectedID = (document.id).toString();
since you already have taken a document you don't have to query it again
final doc = FirebaseFirestore.instance.collection('users').doc('contexts').get();
and
FirebaseFirestore.instance.collection('Users').doc(FirebaseAuth.instance.currentUser.uid).collection('contexts').snapshots(),
These two are contradictory. Is contexts a collection or a document?
Problem solved.
child: Column(
//mainAxisAlignment: MainAxisAlignment.center,
children: [
//FOR CONTEXT
Flexible(child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('Users')
.doc(FirebaseAuth.instance.currentUser.uid)
.collection('contexts')
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
} else if (snapshot.hasData) {
// if connectionState is waiting
if (snapshot.connectionState == ConnectionState.waiting)
{
return Center(child: CircularProgressIndicator());
} else {
for (int i=0;i<snapshot.data.docs.length;i++){
DocumentSnapshot snap = snapshot.data.docs[i];
_contexts.add(snap['context_Name']);
}
}
}
// return widgets and use data
return Column(children:[
TestWidgetContext(),
]); //MyHomePage())
I'm new to the flutter world and mobile app development and struggling with how I should pass data throughout my app. This is my code, How can I pass snapshot data from futurebuilder to another futurebuilder on the same page? help, please
**Widget _buildProgrammCard()**
From this widget Card I need to pass the location id which is in the futurebuilder to another futurebuilder.
Widget _buildProgrammCard() {
return Container(
height: 90,
child:
Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 4,
margin: EdgeInsets.fromLTRB(14, 0, 14, 14),
child:
FutureBuilder(
future: databaseHelper2.Lastlocation(),
builder: (context,snapshot) {
if (snapshot.hasError)
{
print(snapshot.error);
print("there is problem");
}
return snapshot.hasData
? Text("Location :" +snapshot.data.id)
: Center(child: CircularProgressIndicator(
),
);
}
),
),
);
}
Widget build(BuildContext context)
And this is the second Widget that I need to pass the location id into it from another widget.
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
body: FutureBuilder(
future: databaseHelper2.Getweither(location_id),
builder: (context,snapshot) {
if (snapshot.hasError)
{
print(snapshot.error);
print("there is problem !");
}
return snapshot.hasData
? ItemList(list: snapshot.data)
: Center(child: CircularProgressIndicator(
),
);
}
),
);
}
Flutter rebuilds widgets often so FutureBuilder shouldn't call a future function directly. (A widget may call its build function up to 60 times a second.)
Instead a FutureBuilder should only receive a future value from an async function called elsewhere.
In a StatefulWidget, the most common place to initiate long-running operations is in its initState() method.
The location data, retrieved during the first Widget initState, can be passed to the second widget, just like a regular constructor argument.
You'll access it in the 2nd widget's State class with widget.locationId.
import 'package:flutter/material.dart';
class FirstFuturePage extends StatefulWidget {
#override
State<StatefulWidget> createState() => FirstFutureState();
}
class FirstFutureState extends State<FirstFuturePage> {
Future<int> locationId = Future.value(-1);
#override
void initState() {
// TODO: implement initState
super.initState();
someAsyncCall();
}
Future<void> someAsyncCall() async {
// just returns the number 0 after 2 seconds & assigns it to "locationId" var
locationId = Future.delayed(Duration(seconds: 2), () => 0);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: FutureBuilder<int>(
future: locationId,
builder: (context, snapshot) {
int _locationId = snapshot.data;
if (snapshot.hasData)
return SecondWidget(_locationId);
return Text('Looking up location...');
},
),
),
),
);
}
}
class SecondWidget extends StatefulWidget {
final int locationId;
SecondWidget(this.locationId);
#override
_SecondWidgetState createState() => _SecondWidgetState();
}
class _SecondWidgetState extends State<SecondWidget> {
Future<String> weatherData = Future.value('Unknown');
#override
void initState() {
super.initState();
loadWeather(widget.locationId); // Use the locationId passed into widget
}
/// Takes locationId from First widget and looks up weather data for location
Future<void> loadWeather(int locationId) async {
List<String> weatherDataStore = List<String>.from(['Rainy', 'Sunny']);
weatherData = Future.delayed(
Duration(seconds: 2), () => weatherDataStore[locationId]
);
}
#override
Widget build(BuildContext context) {
int _locId = widget.locationId;
return FutureBuilder<String>(
future: weatherData,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('Weather for location $_locId is: ${snapshot.data}');
}
return Text('Loading Weather...');
},
);
}
}
State Management Solutions
When you get tired of passing values around like in the above example, you can use a State Management package or create your own that suits your needs.
Here's a nice overview from Jeff Delaney about various options:
https://fireship.io/lessons/flutter-state-management-guide/
And also check out Get which isn't mentioned in the above:
https://pub.dev/packages/get
Some of the above State management solutions (e.g. Provider) help you use Flutter-native state functionality correctly (because its rather complicated), while others completely avoid that and provide a framework separate from the Widget lifecycle (e.g. Get).
Thanks Baker for your response but not exactly what i meant
these are my two futures and my class
This is my future that returns the Location from her i need to pass the location id to the another future
Future<Location> Lastlocation() async {
final prefs = await SharedPreferences.getInstance();
final key = 'token';
final value = prefs.get(key) ?? 0;
String myUrl = "$serverUrl/location/getlastlocation?token=" + value;
http.Response response = await http.get(
myUrl,
headers: {
'Accept': 'application/json',
//'Authorization': 'token $value'
},
);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
return Location.fromJson(json.decode(response.body));
} else {
// then throw an exception.
throw Exception('Failed to load album');
}
}
This is my future that returns List of weather that depends on location id
Future<List> Getweither(String ID) async {
final prefs = await SharedPreferences.getInstance();
final key = 'token';
final value = prefs.get(key) ?? 0;
String myUrl = "$serverUrl/sensors/getDeviceByid/$ID?token=" + value;
http.Response response = await http.get(myUrl,
headers: {
'Accept': 'application/json',
});
print("myUrldevice :"+myUrl);
print("status :"+response.statusCode.toString());
return json.decode(response.body);
}
This is my class Location
// To parse this JSON data, do
//
// final location = locationFromJson(jsonString);
import 'dart:convert';
List<Location> locationFromJson(String str) => List<Location>.from(json.decode(str).map((x) => Location.fromJson(x)));
String locationToJson(List<Location> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Location {
Location({
this.automaticIrrigation,
this.coordinates,
this.createdDate,
this.sensorIds,
this.id,
this.siteName,
this.description,
this.v,
});
bool automaticIrrigation;
List<double> coordinates;
DateTime createdDate;
List<String> sensorIds;
String id;
String siteName;
String description;
int v;
factory Location.fromJson(Map<String, dynamic> json) => Location(
automaticIrrigation: json["AutomaticIrrigation"],
coordinates: List<double>.from(json["Coordinates"].map((x) => x.toDouble())),
createdDate: DateTime.parse(json["Created_date"]),
sensorIds: List<String>.from(json["Sensor_ids"].map((x) => x)),
id: json["_id"],
siteName: json["SiteName"],
description: json["Description"],
v: json["__v"],
);
Map<String, dynamic> toJson() => {
"AutomaticIrrigation": automaticIrrigation,
"Coordinates": List<dynamic>.from(coordinates.map((x) => x)),
"Created_date": createdDate.toIso8601String(),
"Sensor_ids": List<dynamic>.from(sensorIds.map((x) => x)),
"_id": id,
"SiteName": siteName,
"Description": description,
"__v": v,
};
}
and this is my homePage
import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sidebar_animation/Services/DataHelpers.dart';
import 'package:sidebar_animation/sidebar/sidebar_layout.dart';
import '../bloc.navigation_bloc/navigation_bloc.dart';
import 'package:sidebar_animation/constants.dart';
import 'package:flutter/gestures.dart';
import 'package:sidebar_animation/bloc.navigation_bloc/navigation_bloc.dart';
class HomePage extends StatelessWidget with NavigationStates {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
DatabaseHelper2 databaseHelper2 = new DatabaseHelper2();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
body: ListView(
FutureBuilder(
future: databaseHelper2.Getweither(location_id),
builder: (context,snapshot) {
if (snapshot.hasError)
{
print(snapshot.error);
print("there is problem !");
}
return snapshot.hasData
? ItemList(list: snapshot.data)
: Center(child: CircularProgressIndicator(
),
);
}
),
);
}
Widget _buildProgrammCard() {
return Container(
height: 90,
child:
Card(
semanticContainer: true,
clipBehavior: Clip.antiAliasWithSaveLayer,
color: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
elevation: 4,
margin: EdgeInsets.fromLTRB(14, 0, 14, 14),
child:
FutureBuilder(
// future: databaseHelper.getData(),
future: databaseHelper2.Lastlocation(),
builder: (context,snapshot) {
if (snapshot.hasError)
{
print(snapshot.error);
print("mochkla lenaa *");
}
return snapshot.hasData
? Text("Location :" +snapshot.data.siteName)
: Center(child: CircularProgressIndicator(
),
);
}
),
),
);
}
class ItemList extends StatelessWidget{
List list;
ItemList({this.list});
ScrollController _controller = new ScrollController();
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: list == null ? 0 : list.length,
scrollDirection: Axis.horizontal,
itemExtent: 190.0,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 0, 14),
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
item.storyUrl,
),
fit: BoxFit.cover,
colorFilter: ColorFilter.mode(
Colors.black26,
BlendMode.darken,
),
),
borderRadius: BorderRadius.circular(10.0),
color: Colors.grey,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(
"temp :",
style: TextStyle(color: Colors.white),
),
),
Padding(
padding: EdgeInsets.only(left: 24),
child:
Text(
list[i]['Weither']['Temp'],
),
),
],
),
),
);
},
),
}
}
Finaly i need to get the location id from the first future that return Location to the second future Getweither(String ID) .
I am making an app in the form of a news feed like facebook. I would like to make it user go through contenst in the feed and make them read more detail by clicking on the feed(feedPage to postPage).
I use Firestore as a backend and the provider pattern.
feedPage refers to StreamProvider and myUserData Provider(from main.dart)
Whenever the content is modified or changed or I clicked like, added a comment, it is updated immediately without the need to refresh.
// feed_page.dart
class FeedPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider<List<Post>>.value(
value: firestoreProvider.fetchAllPosts(),
child: Scaffold(
backgroundColor: Colors.grey[200],
appBar: AppBar(
automaticallyImplyLeading: false,
title: GestureDetector(
child: Text('test'),
-----
Container _postActions(
BuildContext context, MyUserData myUserData, Post post) {
return Container(
child: Padding(
padding: const EdgeInsets.fromLTRB(
common_gap, common_gap, common_gap, common_gap),
child: Row(
children: <Widget>[
GestureDetector(
onTap: () {
final route = MaterialPageRoute(
builder: (context) => PostPage(myUserData.data, post));
Navigator.of(context).push(route);
},
the issue is in post_page.dart. When I access to postPage through Navigator, I could see all the details in the postPage. But when I modify something(such as clicking like/heart button, added a comment), I can't see any change in postPage.
// post_page.dart
class PostPage extends StatefulWidget {
final User user;
final Post post;
const PostPage(this.user, this.post, {Key key}) : super(key: key);
#override
_PostPageState createState() => _PostPageState();
}
class _PostPageState extends State<PostPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[200],
appBar: AppBar(
centerTitle: false,
elevation: 0,
actions: <Widget>[
IconButton(
icon: Icon(
Icons.label_outline,
color: Colors.grey[900],
size: 30,
),
onPressed: null,
),
],
),
body: Form(
key: _formkey,
child: SafeArea(
child: Column(
children: <Widget>[
Expanded(
child: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: _postItem(
context, widget.post.title, widget.post.content),
),
SliverToBoxAdapter(
child: _postComments(),
),
],
),
),
_commentField()
],
),
),
),
);
}
Container _postActions(BuildContext context) {
return Container(
child: Padding(
padding: const EdgeInsets.fromLTRB(
common_gap, common_gap, common_gap, common_xl_gap),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
GestureDetector(
onTap: () {},
child: Row(
children: <Widget>[
Image.asset('assets/comment.png',
width: 17, fit: BoxFit.cover),
SizedBox(
width: common_xxs_gap,
),
Text("${widget.post.numOfComments}", // <-------------------------- my issue is here!
style: TextStyle(fontSize: 13.0, color: Colors.black87))
],
),
),
)
);
}
For example, if I added a comment in this page, widget.post.numOfComments in PostPage should be updated immediately, but to get it updated I got to refresh it by going back to feedPage and return to postPage again.
I think it is because I do not set up provider or setState in postPage.
But I actually have no idea of how to use setState with FirebaseProvider.
And if possible, I want to use Provider either since it is nested in the feedPage(I know it is not in the widget tree of feedPage).
// firestore_provider.dart
class FirestoreProvider with Transformer {
final Firestore _firestore = Firestore.instance;
Future<void> attemptCreateUser({String userKey, String phone}) async {
final DocumentReference userRef =
_firestore.collection(COLLECTION_USERS).document(userKey);
final DocumentSnapshot snapshot = await userRef.get();
return _firestore.runTransaction((Transaction tx) async {
if (!snapshot.exists) {
await tx.set(userRef, User.getMapForCreateUser(userKey, phone));
}
});
}
Stream<User> connectMyUserData(String userKey) {
return _firestore
.collection(COLLECTION_USERS)
.document(userKey)
.snapshots()
.transform(toUser);
}
Stream<List<User>> fetchAllUsers() {
return _firestore
.collection(COLLECTION_USERS)
.snapshots()
.transform(toUsers);
}
Future<Map<String, dynamic>> createNewPost(
String postKey, Map<String, dynamic> postData) async {
final DocumentReference postRef = _firestore
.collection(COLLECTION_POSTS)
.document(postKey); // 없으면 자동으로 Reference 생성됨.
final DocumentSnapshot postSnapshot = await postRef.get();
final DocumentReference userRef =
_firestore.collection(COLLECTION_USERS).document(postData[KEY_USERKEY]);
return _firestore.runTransaction((Transaction tx) async {
if (!postSnapshot.exists) {
await tx.update(userRef, {
KEY_MYPOSTS: FieldValue.arrayUnion([postKey])
});
await tx.set(postRef, postData);
}
});
}
Stream<List<Post>> fetchAllPosts() {
return _firestore
.collection(COLLECTION_POSTS)
.orderBy(KEY_POSTTIME, descending: true)
.snapshots()
.transform(toPosts);
}
Future<Map<String, dynamic>> createNewComments(
String postKey, Map<String, dynamic> commentData) async {
final DocumentReference postRef = _firestore
.collection(COLLECTION_POSTS)
.document(postKey); // 없으면 자동으로 Reference 생성됨.
final DocumentSnapshot postSnapshot = await postRef.get();
print(postSnapshot);
final DocumentReference commentRef =
postRef.collection(COLLECTION_COMMENTS).document();
return _firestore.runTransaction((Transaction tx) async {
if (postSnapshot.exists) {
await tx.set(commentRef, commentData);
int numOfComments = postSnapshot.data[KEY_NUMOFCOMMENTS];
await tx.update(postRef, {KEY_NUMOFCOMMENTS: numOfComments + 1});
}
});
}
Stream<List<CommentModel>> fetchAllComments(String postKey) {
return _firestore
.collection(COLLECTION_POSTS)
.document(postKey)
.collection(COLLECTION_COMMENTS)
.orderBy(KEY_COMMENTTIME)
.snapshots()
.transform(toComments);
}
}
FirestoreProvider firestoreProvider = FirestoreProvider();
plz help.. thanks!
Here's my code:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
//import 'package:location/location.dart';
class Home extends StatefulWidget {
Home({Key key, this.title}) : super(key: key);
final String title;
#override
_Home createState() => _Home();
}
class _Home extends State<Home> {
//declared var
String key = "ville";
String txt_ville = "PARIS";
List<String> villes = [];
GlobalKey<ScaffoldState> _drawerKey = GlobalKey();
//Location location;
//LocationData locationData;
//Stream<LocationData> stream;
#override
void initState() {
// TODO: implement initState
getData();
print(villes);
super.initState();
//location = new Location();
//getFirstLocation();
}
/*getFirstLocation() async {
try {
print("Position: ${locationData.latitude}/${locationData.longitude}");
}catch (e) {
print("Error when locating $e");
}
}*/
#override
Widget build(BuildContext context) {
return Scaffold(
key: _drawerKey,
drawer: Drawer(
child: new ListView.builder(
itemCount: villes.length,
itemBuilder: (BuildContext ctx, i) {
return new ListTile(title: new Text(villes[i]));
},
)
),
backgroundColor: Colors.amberAccent,
body: new Column(
children: <Widget>[
Expanded(
flex:4,
child: new Container(
width: double.infinity,
decoration: new BoxDecoration(
color: Colors.white,
borderRadius: new BorderRadius.only(bottomLeft: Radius.circular(120))
),
child: new Column(
children: <Widget>[
new Padding(
padding: EdgeInsets.only(top: 50),
child: new Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
new Padding(padding: EdgeInsets.only(left: 20),
child: new IconButton(icon: new Icon(Icons.dehaze, size: 30, color: Colors.black,), onPressed: () {
_drawerKey.currentState.openDrawer();
})),
new Padding(
padding: EdgeInsets.only(left: 30),
child: new Text("TheWeatherApp", style: new TextStyle(
fontSize: 40
),),
),
]),
)
],
),
)
),
Expanded(
flex: 1,
child: new Container(
),
)
],
)
);
}
Future<Null> addTown() async{
return showDialog(barrierDismissible: true, context: context, builder: (BuildContext buildcontext) {
return new SimpleDialog(
contentPadding: EdgeInsets.all(20.0),
title: Text("Add a town"),
children: <Widget>[
new RaisedButton(onPressed: () {
}, child: new Text("Auto locate me"),),
new TextField(
decoration: new InputDecoration(
labelText: "Ville"
),
onSubmitted: (s) {
setData(s);
Navigator.pop(context);
},
)
],
);
});
}
void getData() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
List<String> liste = await sharedPreferences.getStringList(key);
if (liste != null) {
setState(() {
villes = liste;
});
}
}
void setData(String str) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
villes.add(str);
await sharedPreferences.setStringList(key, villes);
getData();
}
void deleteData(String str) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
villes.remove(str);
await sharedPreferences.setStringList(key, villes);
}
}
I'm still a beginner on Flutter and I'm trying to understand why when I launch my application on the emulator and open my drawer with the iconbutton I get this error: Pastbinlink to error
If I deliberately create an error in the code like removing a parenthesis and do a hotreload, and I put the parenthesis back and do a hotreload again then my code works and the listview is displayed...
I have a theory that it's my getData function that initializes the variable villes that doesn't work...
I thank you in advance for any answer!
I can't comment, so I am writing it as an answer.
I haven't tried your code, and I am not sure if it will work, however,
You should try to use FutureBuilder
SharedPreferences.getInstance() is a future that will have value later on, and when you try to build your Drawer, it tries to access a value that doesn't exist.
Instead, if you try it like this. Your drawer will be created once your data is retrieved.
drawer: Drawer(
child: new FutureBuilder<List<String>>(
future: getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
if (snapshot.data.length == 0) {
return Center(child: Text("No data found."));
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext ctx, i) {
return new ListTile(title: new Text(villes[i]));
},
)
}
} else if (snapshot.hasError) {
return checkError();
}
// By default, show a loading spinner.
return Center(child: CircularProgressIndicator());
})
),