Pass data from dropdown Button Flutter - firebase

i wanted to get the dropdown widget out of there and put it to another place and pass the data to the another page which firestore use it to Query! ,tried many ways:( help! thanks.
String _currentlySelectedlanguage="USA";
final List<String> _dropdownValueslanguage = ["USA", "India"];
Widget dropdownWidgetlanguage() {
return DropdownButton(
isExpanded: false,
value: _currentlySelectedlanguage,
onChanged: (String newvalue) {
setState(() {
_currentlySelectedlanguage = newvalue;
});
},
items: _dropdownValueslanguage
.map((value) => DropdownMenuItem(
child: Text(value),
value: value,
))
.toList(),
);
}
Future<List<DocumentSnapshot>> getData(String filter) async {
final firestore = FirebaseFirestore.instance;
QuerySnapshot snapshot = await firestore
.collection('slideshow')
.where("lang", isEqualTo: _currentlySelectedlanguage)
.get();
return snapshot.docs;
}
}

Try below code
class Code extends StatefulWidget {
#override
_CodeState createState() => _CodeState();
}
String currentLanguage; //updated
class _CodeState extends State<Code> {
#override
Widget build(BuildContext context) {
return Column(
children: [
dropdownWidgetlanguage(),
FutureBuilder<List<DocumentSnapshot>>(
future: getData(_currentlySelectedlanguage),
builder: (context, snapshot) {
if(snapshot.hasData){
return ListView.builder(
itemCount: snapshot.data.length,
shrinkWrap: true,
itemBuilder: (context, index) => snapshot.data[index].data()['name'],);
}else{
return Container();
}
},)
],
);
}
Widget dropdownWidgetlanguage() {
return DropdownButton(
isExpanded: false,
value: _currentlySelectedlanguage,
onChanged: (String newvalue) {
setState(() {
_currentlySelectedlanguage = newvalue;
currentLanguage = newvalue; //updated
});
},
items: _dropdownValueslanguage
.map((value) => DropdownMenuItem(
child: Text(value),
value: value,
))
.toList(),
);
}
String _currentlySelectedlanguage;
final List<String> _dropdownValueslanguage = ["USA", "India"];
Future<List<DocumentSnapshot>> getData(String filter) async {
final firestore = FirebaseFirestore.instance;
QuerySnapshot snapshot = await firestore
.collection('slideshow')
.where("lang", isEqualTo: filter)
.get();
return snapshot.docs;
}
}
class SecondClass extends StatefulWidget {
#override
_Class2State createState() => _Class2State();
}
class _Class2State extends State<SecondClass> {
#override
Widget build(BuildContext context) {
return Container(
child: Text(currentLanguage),
);
}
}

Related

Lazy Loading list view builder in Flutter with Helper.Provider-Model

what I'm trying to do is use lazy loading of elements in list view builder in flutter.
I used also the Provider, helpers and model paradigma and I'm trying to load some dishes from my db in firestore.
I found different solution for implementing lazy loading such as this. But I obtain an error.
Below the code:
Helpers
class DishServices{
String collection = "dishes";
FirebaseFirestore _firestore = FirebaseFirestore.instance;
String subcollection = "ingredients";
List<DishModel> dishes = [];
QuerySnapshot collectionState;
Future<List<DishModel>> getLazyDishes() async
{
var collectionData = _firestore.collection(collection).orderBy("name").limit(2);
fetchDocuments(collectionData);
return dishes;
}
Future<List<DishModel>> getLazyDishesNext() async {
// Get the last visible document
var lastVisible = collectionState.docs[collectionState.docs.length-1];
var collectionData = FirebaseFirestore.instance
.collection(collection)
.orderBy("name").startAfterDocument(lastVisible).limit(3);
fetchDocuments(collectionData);
return dishes;
}
fetchDocuments(Query collection){
collection.get().then((value) {
collectionState = value; // store collection state to set where to start next
value.docs.forEach((element) {
dishes.add(DishModel.fromSnapshot(element));
});
});
}
}
Provider
class DishProvider with ChangeNotifier{
DishServices _dishServices = DishServices();
List<DishModel> dishes = [];
List<DishModel> dishesLazyNext = [];
DishProvider.initialize(){
_loadDishes();
// searchIngredient(ingredientName:"cime");
}
_loadDishes() async{
dishes = await _dishServices.getLazyDishes();
notifyListeners();
}
Future loadNextLazyDishes()async{
dishesLazyNext = await _dishServices.getLazyDishesNext();
dishes = dishes + dishesLazyNext;
notifyListeners();
}
}
Main.dart
oid main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MultiProvider(providers: [
ChangeNotifierProvider.value(value: DishProvider.initialize())
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'MyApp',
theme: ThemeData(
primarySwatch: Colors.red,
),
home: ScreensController()
)));
}
Finally my build is :
class _MainHomeState extends State<MainHome> {
ScrollController scrollController = ScrollController();
#override
void initState() {
super.initState();
final dishProvider = Provider.of<DishProvider>(context);
scrollController.addListener(() {
if (scrollController.position.atEdge) {
if (scrollController.position.pixels == 0)
print('ListView scroll at top');
else {
print('ListView scroll at bottom');
dishProvider.loadNextLazyDishes(); // Load next documents
}
}
});
}
#override
Widget build(BuildContext context) {
final dishProvider = Provider.of<DishProvider>(context);
//atest.loadA();
return Scaffold(
body: SafeArea(
child: ListView(
children: <Widget>[
OtherElement(),
Container(
child: ListView.builder(
controller: scrollController,
itemCount: dishProvider.dishes.length,
shrinkWrap: true,
itemBuilder: (_,index){
return DishWidget(
dishModel: dishProvider.dishes[index],
);
}),
),
],
),
),
);
}
}
this is the error that return when I try to run the app
How I can fix it?
Just move the scroll listener to your build:
class _MainHomeState extends State<MainHome> {
ScrollController scrollController = ScrollController();
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
final dishProvider = Provider.of<DishProvider>(context);
scrollController.addListener(() {
if (scrollController.position.atEdge) {
if (scrollController.position.pixels == 0)
print('ListView scroll at top');
else {
print('ListView scroll at bottom');
dishProvider.loadNextLazyDishes(); // Load next documents
}
}
});
//atest.loadA();
return Scaffold(
body: SafeArea(
child: ListView(
children: <Widget>[
OtherElement(),
Container(
child: ListView.builder(
controller: scrollController,
itemCount: dishProvider.dishes.length,
shrinkWrap: true,
itemBuilder: (_,index){
return DishWidget(
dishModel: dishProvider.dishes[index],
);
}),
),
],
),
),
);
}
}

Trying to load the data as I scroll my listview

I am trying to make the page load data as i scroll, so I made a scroll listener in my list builder then i made a fetch next function in my future builder.
But how can i call my fetchNext function which is in my future builder to call in my void scrollListener() {}.
Is it even right to paginate the data like this. Please help.
This is the list builder
class PostGridScreen extends StatefulWidget {
final String type, city, state;
PostGridScreen({this.type, this.city, this.state});
#override
_PostGridScreenState createState() => _PostGridScreenState();
}
class _PostGridScreenState extends State<PostGridScreen>
with AutomaticKeepAliveClientMixin {
Future _getPosts;
int documentLimit = 5;
bool _hasNext = true;
bool _isFetchingUsers = false;
final _usersSnapshot = <DocumentSnapshot>[];
final scrollController = ScrollController();
var refreshKey = GlobalKey<RefreshIndicatorState>();
#override
void initState() {
_fetchPosts();
// scrollController.addListener(scrollListener);
super.initState();
}
#override
void dispose() {
scrollController.dispose();
super.dispose();
}
void scrollListener() {}
Future _fetchPosts() {
return _getPosts = Provider.of<BaseProvider>(context, listen: false)
.getPosts(widget.type, widget.city, widget.state, documentLimit);
}
Future<Null> refreshList() async {
refreshKey.currentState?.show(atTop: false);
await Future.delayed(Duration(seconds: 2));
setState(() {
_fetchPosts();
});
return null;
}
#override
Widget build(BuildContext context) {
super.build(context);
return RefreshIndicator(
onRefresh: () => refreshList(),
child: FutureBuilder(
key: PageStorageKey('Page1'),
future: _getPosts,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
controller: scrollController,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
//Fetch Next
Future fetchNextUsers() async {
if (_isFetchingUsers) return;
_isFetchingUsers = true;
try {
_getPosts =
Provider.of<BaseProvider>(context, listen: false)
.getPosts(
widget.type,
widget.city,
widget.state,
documentLimit,
startAfter: _usersSnapshot.isNotEmpty
? _usersSnapshot.last
: null);
_usersSnapshot.addAll(snapshot.data);
if (snapshot.data.length < documentLimit)
_hasNext = false;
} catch (e) {}
_isFetchingUsers = false;
}
//Tile
return GestureDetector(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => PostDetailScreen(
post: snapshot.data[index],
),
));
},
child: Padding(
padding: EdgeInsets.only(
top: 10,
bottom: 3.0,
right: 20.0,
left: 20.0,
),
child: Container(
decoration: BoxDecoration(
boxShadow: [
BoxShadow(
color: Colors.black12,
offset: const Offset(
5.0,
5.0,
),
blurRadius: 10.0,
spreadRadius: 2.0,
), //BoxShadow
],
color: Colors.blue,
borderRadius: BorderRadius.circular(20.0),
image: DecorationImage(
image: NetworkImage(
snapshot.data[index]["imageUrl"],
),
fit: BoxFit.cover,
),
),
height: 190.0,
width: 100,
),
),
);
},
);
} else {
return Center(
child: Text("Loading..."),
);
}
}),
);
}
#override
bool get wantKeepAlive => true;
}
This is the base provider
class BaseProvider with ChangeNotifier {
final CollectionReference postsCollection =
FirebaseFirestore.instance.collection("posts");
//Fetching Data
Future getPosts(String type, String city, String state, int limit,
{DocumentSnapshot startAfter}) async {
try {
if (startAfter == null) {
QuerySnapshot<Map<String, dynamic>> response = await FirebaseFirestore
.instance
.collection("posts")
.where('type', isEqualTo: type)
.where('sellercity', isEqualTo: city)
.where('sellerstate', isEqualTo: state)
.limit(limit)
.get();
return response.docs;
} else {
QuerySnapshot<Map<String, dynamic>> response = await FirebaseFirestore
.instance
.collection("posts")
.where('type', isEqualTo: type)
.where('sellercity', isEqualTo: city)
.where('sellerstate', isEqualTo: state)
.startAfterDocument(startAfter)
.limit(limit)
.get();
return response.docs;
}
} catch (e) {
print(e.toString());
return e;
}
}
}
if I understood correctly you want that your app doesn't load and render everything in a list at once and render them just in time it is shown to the user.
for achieving this you have to implement lazy loading which ListView.builder do this completely on its own so you don't need to worry about it

Store documents from firestore in list

I am trying to retrieve documents from collection called "Org" and store it in list to display in the dropdownmenu, but the list returns [Instance of 'QueryDocumentSnapshot', Instance of 'QueryDocumentSnapshot'] and thus i get following error:
The method 'map' was called on null.
Receiver: null
Tried calling: map(Closure: (dynamic) => DropdownMenuItem<dynamic>)
This is the code i implemented:
void organisation() async {
await Firebase.initializeApp();
QuerySnapshot querySnapshot = await FirebaseFirestore.instance.collection("Org").get();
final List<DocumentSnapshot> list = querySnapshot.docs;
print(list);
list.forEach((data) => print(data));
}
#override
initState() {
Firebase.initializeApp();
organisation();
super.initState();
}
This is how i implemented dropdownmenu:
DropdownButtonFormField(
validator: (value) => value == "Select" ? 'Field required' : null,
value: _selectorg,
onChanged: (val) => setState(() => _selectorg = val),
items: list.map(
(item) {
return DropdownMenuItem(
child: Text('$item'),
value: item,
);
},
).toList(),
hint: Text(
"SELECT ORGANISATION",
style: TextStyle(color: Colors.white)),
),
Try this,
#override
initState() {
super.initState();
Firebase.initializeApp();
organisation();
}
And,
Declare your list outside the function
final List<DocumentSnapshot> list;
void organisation() async {
await Firebase.initializeApp();
QuerySnapshot querySnapshot = await FirebaseFirestore.instance.collection("Org").get();
list = querySnapshot.docs;
print(list);
list.forEach((data) => print(data));
}
Update
StreamBuilder(
stream: FirebaseFirestore.instance.collection("Org").get();
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshots) {
if (snapshots.connectionState == ConnectionState.active &&
snapshots.hasData) {
print(snapshots.data);
return ListView.builder(
itemCount: snapshots.docs.length,
itemBuilder: (BuildContext context, int index) {
DocumentSnapshot doc = snapshots.data[index];
Map data= doc.data;//This is your data
return Text(
data.toString(),
);
},
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),

The getter 'uid' was called on null

Guys I have this login page with email and password and when the user is succesfully logged it takes him to a chatscreen. On the chatscreen I need to access users uid to check who is sending the message, but I am getting the "The getter 'uid' was called on null.". I am using bloc pattern to login with validators. How can I fix this situation?
class LoginScreen extends StatefulWidget {
FirebaseUser user;
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _loginBloc = LoginBloc();
#override
void initState() {
super.initState();
_loginBloc.outState.listen((state) async {
switch (state) {
case LoginState.SUCCESS:
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => ComercialUserScreen()));
break;
case LoginState.FAIL:
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Erro'),
content: Text('Revise seu email e senha'),
));
break;
case LoginState.LOADING:
case LoginState.IDLE:
}
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<LoginState>(
stream: _loginBloc.outState,
initialData: LoginState.LOADING,
// ignore: missing_return
builder: (context, snapshot) {
print(snapshot.data);
switch (snapshot.data) {
case LoginState.LOADING:
return Center(
child: CircularProgressIndicator(),
);
case LoginState.FAIL:
case LoginState.SUCCESS:
case LoginState.IDLE:
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
InputField(
icon: Icons.person_outline,
hint: 'Usuário',
obscure: false,
stream: _loginBloc.outEmail,
onChanged: _loginBloc.changeEmail,
),
InputField(
icon: Icons.lock_outline,
hint: 'Senha',
obscure: true,
stream: _loginBloc.outEmail,
onChanged: _loginBloc.changePassword,
),
SizedBox(
height: 32,
),
StreamBuilder<bool>(
stream: _loginBloc.outSubmitValid,
builder: (context, snapshot) {
return RaisedButton(
child: Text("Entrar"),
onPressed:
snapshot.hasData ? _loginBloc.submit : null,
);
})
],
);
}
}),
);
}
}
class LoginBloc extends BlocBase with LoginValidators{
FirebaseUser _currentUser;
final _emailController = BehaviorSubject<String>();
final _passwordController = BehaviorSubject<String>();
final _stateController = BehaviorSubject<LoginState>();
Stream<String> get outEmail => _emailController.stream.transform(validateEmail);
Stream<String> get outPassword =>_passwordController.stream.transform(validatePassword);
Stream<LoginState> get outState => _stateController.stream;
Stream<bool> get outSubmitValid => Observable.combineLatest2(
outEmail, outPassword, (a, b) => true);
Function(String) get changeEmail => _emailController.sink.add;
Function(String) get changePassword => _passwordController.sink.add;
StreamSubscription _streamSubscription;
LoginBloc(){
_streamSubscription = FirebaseAuth.instance.onAuthStateChanged.listen((user) async {
if(user != null) {
if(await verifyAdmins(user)) {
_stateController.add(LoginState.SUCCESS);
} else {
FirebaseAuth.instance.signOut();
_stateController.add(LoginState.FAIL);
}
} else {
_stateController.add(LoginState.IDLE);
}
});
}
void submit (){
final email = _emailController.value;
final password = _passwordController.value;
_stateController.add(LoginState.LOADING);
FirebaseAuth.instance.signInWithEmailAndPassword(
email: email,
password: password
).catchError((e) {
_stateController.add(LoginState.FAIL);
});
}
Future<bool> verifyAdmins (FirebaseUser user) async {
return await Firestore.instance.collection('users').document(user.uid).get().then((doc)
{
if(doc.data != null){
return true;
} else {
return false;
}
}).catchError((e) {
return false;
});
}
#override
void dispose() {
_emailController.close();
_passwordController.close();
_stateController.close();
_streamSubscription.cancel();
// TODO: implement dispose
}
}
class _AdminChatState extends State<AdminChat> {
bool _isLoading = false;
void _sendMessage({String text, File imgFile}) async {
DocumentSnapshot snapshot;
FirebaseUser user = await FirebaseAuth.instance.currentUser();
Map<String, dynamic> data = {
"uid" : user.uid,
'name' : user.displayName,
'photo' : user.photoUrl,
'time' : Timestamp.now()
};
if (imgFile != null){
StorageUploadTask task = FirebaseStorage.instance.ref().child('users').child(
DateTime.now().millisecondsSinceEpoch.toString()
).putFile(imgFile);
setState(() {
_isLoading = true;
});
StorageTaskSnapshot taskSnapshot = await task.onComplete;
String url = await taskSnapshot.ref.getDownloadURL();
data['imgUrl'] = url;
setState(() {
_isLoading = false;
});
}
if(text != null) data["text"] = text;
Firestore.instance.collection("users").document(snapshot.documentID)
.collection('messages').add(data);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('hello'),
),
body: Column(
children: <Widget>[
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream:
Firestore.instance.collection('users').document(widget.snapshot.documentID)
.collection('messages').orderBy('time').snapshots(),
builder: (context, snapshot){
switch(snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return Center(
child: CircularProgressIndicator(),
);
default:
List<DocumentSnapshot> documents =
snapshot.data.documents.reversed.toList();
return ListView.builder(
itemCount: documents.length,
reverse: true,
itemBuilder: (context, index){
// ignore: unrelated_type_equality_checks
return ChatMessage(documents[index].data, true);
});
}
},
),
),
_isLoading ? LinearProgressIndicator() : Container(),
TextComposer(_sendMessage),
],
),
);
}
}
The command await FirebaseAuth.instance.currentUser(); in the _AdminChatState doesn't return any data so when you try to insert data based on the user is returns null. This happens because you can't use await while the state hasn't initialized. If that happened the entire class would wait with nothing show until the user was returned. Furthermore, you use a storage query with the same logic. If you want a state widget to run something when it starts up you can use the initState() function. If you want do display a progress bar while waiting for the data to return a FutureBuilder or StreamBuilder are great choices.

Set value of a switch with Firestore boolean

I'm making a test app that switches a boolean value in Firestore. I need the app to retrieve the initial value of the switch from Firestore. Through my method, I get the error type 'Future' is not a subtype of type 'bool'. The Boolean value is called LEDOn. Here is my app:
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyAppState();
}
}
class _MyAppState extends State<MyApp> {
final db = Firestore.instance;
void updateLED(bool newValue) async {
await db
.collection('LEDStatus')
.document('LEDStatus')
.updateData({'LEDOn': newValue});
}
Future<dynamic> checkLEDStatus() async {
DocumentSnapshot snapshot =
await db.collection('LEDStatus').document('LEDStatus').get();
return snapshot.data['LEDOn'];
}
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(brightness: Brightness.dark),
home: Scaffold(
appBar: AppBar(
title: Text('Thingspeak'),
),
body: Column(
children: <Widget>[
Switch(
value: checkLEDStatus(),
onChanged: (bool newValue) {
setState(() {
print(newValue);
updateLED(newValue);
});
})
],
),
),
);
}
}
Use a FutureBuilder. Also, change your checkLEDStatus() return type to Future<bool>.
FutureBuilder(
future: checkLEDStatus(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
bool result = snapshot.data;
return Switch(value: result, onChanged: (bool newVal) {
setState(() => result = newVal);
updateLED(newVal);
});
} else {
return Center(child: CircularProgressIndicator());
}
}
)
The Switch widget has the property value which takes a value of type bool. Currently in your code you are returning a Future and that's why you get that error. It should be like this:
Switch(
value: isSwitched,
onChanged: (value) {
setState(() {
isSwitched = value;
});
},
You can retrieve the boolean value from firestore inside the initState and then assign it to the property value. Then when the user switches inside setState() call the update method and do isSwitched = newValue

Resources