Related
I'm building a chat app with Firebase in flutter and I want to be able to search from a list of users,. I also want that when no text is typed, no user should be shown
I tried a lot of things but I never got it right. This is what I did :
search_page.dart:
class SearchPage extends StatefulWidget {
const SearchPage({Key? key}) : super(key: key);
#override
State<SearchPage> createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
TextEditingController searchController = TextEditingController();
#override
void initState() {
super.initState();
searchController.addListener(_onSearchChanged);
}
_onSearchChanged() {
print(searchController.text);
}
#override
void dispose() {
searchController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return StreamProvider<List<AppUserData>>.value(
initialData: [],
value: DatabaseService().users,
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: dDarkGrey,
body:
Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
const SizedBox(
height: 3,
),
Stack(
alignment: Alignment.center,
children: [
Container(
height: 90,
decoration: BoxDecoration(color: dDarkGrey, boxShadow: [
BoxShadow(
color: dBlack.withOpacity(0.16),
spreadRadius: 3,
blurRadius: 3,
offset: const Offset(0, 4))
]),
),
Column(
children: [
const SizedBox(
height: 20,
),
Row(
children: [
IconButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => const HomePage()));
},
icon: const Icon(SocketIconv2.ic_back),
color: dLightGrey,
),
SizedBox(
width: MediaQuery.of(context).size.width - 60,
child: TextField(
enabled: true,
controller: searchController,
style: const TextStyle(color: dLightGrey),
decoration: const InputDecoration(
contentPadding:
EdgeInsets.fromLTRB(0, 0, 0, 0),
filled: true,
fillColor: dDarkGrey,
prefixIcon: Icon(
SocketIconv2.ic_search,
color: dLightGrey,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(15))),
hintStyle: TextStyle(
color: dLightGrey,
fontFamily: 'SegoeUI',
fontSize: 18,
fontWeight: FontWeight.w300),
hintText: 'Search',
))),
],
),
],
),
],
),
// search bar
SizedBox(
height: MediaQuery.of(context).size.height - 95,
width: MediaQuery.of(context).size.width,
child: SearchList(
controller: searchController,
),
)
])),
);
}
}
search_list.dart:
class SearchList extends StatelessWidget {
SearchList({Key? key, required this.controller}) : super(key: key);
final TextEditingController controller;
#override
Widget build(BuildContext context) {
final users = Provider.of<List<AppUserData>>(context);
return Scaffold(
backgroundColor: Colors.transparent,
body: ListView.separated(
itemBuilder: (context, index) {
if (controller.text.isEmpty) {
return Container();
}
if (controller.text.isNotEmpty) {
return searchAccount(context, name, username, users[index]);
}
if (users[index].name.startsWith(controller.text.toLowerCase())) {
return searchAccount(context, name, username, users[index]);
} else {
return Container();
}
},
itemCount: users.length,
separatorBuilder: (context, index) => const SizedBox(height: 20),
));
}
}
search_account.dart:
Widget searchAccount(
BuildContext context, String name, String username, AppUserData users) {
return Row(
children: [
const SizedBox(
width: 20,
),
Row(
children: [
ClipOval(
child: Image.asset(
imagePp,
scale: 9,
),
),
const SizedBox(
width: 30,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(users.name,
style: SegoeUiPreset(dLightGrey).customTileName()),
Text(users.username,
style: SegoeUiPreset(dLightGrey).customTileSubtitle())
],
)
],
),
],
);
}
user.dart:
class AppUser {
final String? uid;
AppUser({this.uid});
}
class AppUserData {
final String? uid;
final String name;
final String username;
AppUserData({this.uid, required this.name, required this.username});
}
database.dart:
class DatabaseService {
final String? uid;
DatabaseService({this.uid});
final CollectionReference chatRoomCollection =
FirebaseFirestore.instance.collection("chatrooms");
final CollectionReference userCollection =
FirebaseFirestore.instance.collection("users");
Future<void> saveUser(String name, String username) async {
return await userCollection
.doc(uid)
.set({'name': name, 'username': username});
}
AppUserData _userFromSnapshot(DocumentSnapshot snapshot) {
return AppUserData(
name: snapshot['name'],
uid: snapshot.id,
username: snapshot['username']);
}
Stream<AppUserData> get user {
return userCollection.doc(uid).snapshots().map(_userFromSnapshot);
}
List<AppUserData> _userListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((doc) {
return _userFromSnapshot(doc);
}).toList();
}
Stream<List<AppUserData>> get users {
return userCollection.snapshots().map(_userListFromSnapshot);
}
}
Got any hints ? I'm a begginner :/
Thank you in advance :)
I tried to fetch data from firestore to chip widgets but then show "LateInitializationError". And also chips should be can multi selection(select many chips). And also how to align 4 chips in a row like this example?I my code I think chips are show like ListView.
error..
I mean like this..
my code..
class uitry extends StatefulWidget {
const uitry({Key? key}) : super(key: key);
#override
State<uitry> createState() => _uitryState();
}
#override
Future<List<Words12>> fetchRecords() async {
var records = await FirebaseFirestore.instance.collection('12words').get();
return mapRecords(records);
}
List<Words12> mapRecords(QuerySnapshot<Map<String, dynamic>> records) {
var _list = records.docs
.map(
(words12) => Words12(
id: words12.id,
wordName: words12['wordName'],
categoryName: words12['categoryName'],
),
)
.toList();
return _list;
}
late int defaultChoiceIndex;
#override
void initState() {
initState();
defaultChoiceIndex = 0;
}
child: SizedBox(
width: width * 0.94,
height: height * 0.30,
child: FutureBuilder<List<Words12>>(
future: fetchRecords(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<Words12> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return (ChoiceChip(
label: Text(data[index].wordName),
selected: defaultChoiceIndex == index,
selectedColor: Colors.deepPurple,
onSelected: (value) {
setState(() {
defaultChoiceIndex =
value ? index : defaultChoiceIndex;
});
},
// backgroundColor: color,
elevation: 1,
padding: const EdgeInsets.symmetric(
horizontal: 5.0),
));
},
);
}
}),
),
#override
void initState() {
initState();
defaultChoiceIndex = 0;
}
Should be:
#override
void initState() {
super.initState();
defaultChoiceIndex = 0;
}
I believe it will initialize your defaultChoiceIndex then.
For the alignment of chips: Wrap your ChoiceChip in the ListView.builder in a Row(), with a mainAxisAlignment of your choosing:
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ChoiceChip(etc.),
],
),
I tried it .Now it working..
code
SizedBox(
width: width * 0.94,
height: height * 0.30,
child: Column(
children: <Widget>[
const SizedBox(height: 16),
Wrap(
children: hobbyList.map(
(hobby) {
bool isSelected = false;
if (selectedHobby!.contains(hobby)) {
isSelected = true;
}
return GestureDetector(
onTap: () {
if (!selectedHobby!.contains(hobby)) {
if (selectedHobby!.length < 50) {
selectedHobby!.add(hobby);
setState(() {});
print(selectedHobby);
}
} else {
selectedHobby!.removeWhere(
(element) => element == hobby);
setState(() {});
print(selectedHobby);
}
},
child: Container(
margin: const EdgeInsets.symmetric(
horizontal: 5, vertical: 4),
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 5, horizontal: 12),
decoration: BoxDecoration(
color: isSelected
? HexColor('#F5F185')
: HexColor('#D9D9D9'),
borderRadius:
BorderRadius.circular(18),
border: Border.all(
color: isSelected
? HexColor('#F5F185')
: HexColor('#D9D9D9'),
width: 2)),
child: Text(
hobby,
style: TextStyle(
color: isSelected
? Colors.black
: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600),
),
),
),
);
},
).toList(),
),
],
),
),
class _uitryState extends State<uitry> {
List<String> hobbyList = [
'Shopping',
'Brunch',
'Music',
'Road Trips',
'Tea',
'Trivia',
'Comedy',
'Clubbing',
'Drinking',
'Wine',
];
List<String>? selectedHobby = [];
I tried to fetch data from firestore to chips but the console showed an empty array. And when click chips colour change to blue colour.To fetch data I used model that's class name "Words12"
My collection name is "12words" at that collection have two fields "wordName"and "categoryName". I want display only "wordName" as chips
this..
I/flutter (14273): []
my full code
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:hexcolor/hexcolor.dart';
import 'package:kathana/utils/config.dart';
import '../screens/wordsBefore18/database/words12model.dart';
class uitry4 extends StatefulWidget {
const uitry4({Key? key}) : super(key: key);
#override
State<uitry4> createState() => _uitry4State();
}
class _uitry4State extends State<uitry4> {
//list
List<Words12> wordList = [];
//collection path
Future<List<Words12>> fetchRecords() async {
var records = await FirebaseFirestore.instance.collection('12words').get();
return mapRecords(records);
}
List<Words12> mapRecords(QuerySnapshot<Map<String, dynamic>> records) {
var _wordList =
records.docs.map((data) => Words12.fromJson(data.data())).toList();
for (var element in _wordList) {
print("name = ${element.wordName}");
}
print("length = ${_wordList.length}");
return _wordList;
}
#override
void initState() {
super.initState();
print(wordList);
}
List<String> selectedWord = [];
List<String>? deSelectedWord = [];
#override
Widget build(BuildContext context) {
double height = MediaQuery.of(context).size.height;
double width = MediaQuery.of(context).size.width;
return Scaffold(
body: Container(
decoration: const BoxDecoration(
image: DecorationImage(
image: AssetImage(Config.app_background4), fit: BoxFit.fill),
),
child: SafeArea(
child: Center(
child: Column(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 14, right: 0),
child: Column(
children: [
SizedBox(
width: width * 0.94,
height: height * 0.30,
child: Column(
children: <Widget>[
const SizedBox(height: 16),
FutureBuilder<List<Words12>>(
future: fetchRecords(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
wordList = snapshot.data ?? [];
return Wrap(
children: wordList.map(
(word) {
bool isSelected = false;
if (selectedWord!
.contains(word.wordName)) {
isSelected = true;
}
return GestureDetector(
onTap: () {
if (!selectedWord!
.contains(word.wordName)) {
if (selectedWord!.length < 50) {
selectedWord!.add(word.wordName);
deSelectedWord!.removeWhere(
(element) =>
element == word.wordName);
setState(() {});
print(selectedWord);
}
} else {
selectedWord!.removeWhere(
(element) =>
element == word.wordName);
deSelectedWord!.add(word.wordName);
setState(() {
// selectedHobby.remove(hobby);
});
print(selectedWord);
print(deSelectedWord);
}
},
child: Container(
margin: const EdgeInsets.symmetric(
horizontal: 5, vertical: 4),
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 5, horizontal: 12),
decoration: BoxDecoration(
color: isSelected
? HexColor('#0000FF')
: HexColor('#D9D9D9'),
borderRadius:
BorderRadius.circular(18),
border: Border.all(
color: isSelected
? HexColor('#0000FF')
: HexColor('#D9D9D9'),
width: 2)),
child: Text(
word.wordName,
style: TextStyle(
color: isSelected
? Colors.black
: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600),
),
),
),
);
},
).toList(),
);
}
}),
],
),
),
],
),
),
],
))),
),
);
}
}
model
import 'dart:convert';
Words12 words12FromJson(String str) => Words12.fromJson(json.decode(str));
String words12ToJson(Words12 data) => json.encode(data.toJson());
class Words12 {
Words12({
required this.id,
required this.wordName,
required this.categoryName,
});
String id;
String wordName;
String categoryName;
factory Words12.fromJson(Map<String, dynamic> json) => Words12(
id: json["id"],
wordName: json["wordName"],
categoryName: json["categoryName"],
);
Map<String, dynamic> toJson() => {
"id": id,
"wordName": wordName,
"categoryName": categoryName,
};
}
How I get data from firestore?
Try change mapRecords() to this :
List<Words12> mapRecords(QuerySnapshot<Map<String, dynamic>> records) {
var _wordList = records.docs
.map(
(data) => Words12.fromJson(data.data())
)
.toList();
return _wordList;
}
and also change your if condition in your FutureBuilder:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
wordList = snapshot.data ?? []; //<--- change this
...
}
also change wordList to this:
class _uitry4State extends State<uitry4> {
List<Words12> wordList = []; //<--- change this
...
}
and this:
children: wordList.map(
(word) {
bool isSelected = false;
if (selectedWord!.contains(word.wordName)) {
isSelected = true;
}
return GestureDetector(
onTap: () {
if (!selectedWord!.contains(word.wordName)) {
if (selectedWord!.length < 50) {
selectedWord!.add(word.wordName);
deSelectedWord!.removeWhere(
(element) => element == word.wordName);
setState(() {});
print(selectedWord);
}
} else {
selectedWord!.removeWhere(
(element) => element == word.wordName);
deSelectedWord!.add(word.wordName);
setState(() {
// selectedHobby.remove(hobby);
});
print(selectedWord);
print(deSelectedWord);
}
},
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 4),
child: Container(
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 12),
decoration: BoxDecoration(
color: isSelected ? HexColor('#0000FF'): HexColor('#D9D9D9'),
borderRadius: BorderRadius.circular(18),
border: Border.all(
color: isSelected ? HexColor('#0000FF') : HexColor('#D9D9D9'),
width: 2)),
child: Text(
word.wordName,
style: TextStyle(color: isSelected
? Colors.black
: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w600),
),
),
).toList(),
also change your model class to this:
factory Words12.fromJson(Map<String, dynamic> json) => Words12(
id: json["id"] ?? "",
wordName: json["wordName"]?? "",
categoryName: json["categoryName"]?? "",
);
Just convert
List<Words12> mapRecords(QuerySnapshot<Map<String, dynamic>> records) {
var wordList = records.docs
.map(
(words12) => Words12(
id: words12.id,
wordName: words12['wordName'],
categoryName: words12['categoryName'],
),
)
.toList();
return wordList;
}
to
List<Words12> mapRecords(QuerySnapshot<Map<String, dynamic>> records) {
var wordList = records.docs
.map(
(words12) {
print(words12);
return
Words12(
id: words12.id,
wordName: words12['wordName'],
categoryName: words12['categoryName']
);}
.toList();
return wordList;
}
and check what you get , also on a sidenote the way your using FutureBuilder is not ideal
Future _fetch = Future(() async {
var records = await FirebaseFirestore.instance.collection('12words').get();
return mapRecords(records);
})
suddently from nowhere i came up with provider not re rendering my home page when it's updated. I've inspected it and it IS UPDATED. It has newer data when i change it in firebase but the UI won't re-render showing the new data. That's my code:
Main function
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:my_event_app/pages/HomePage/home_page.dart';
import 'package:my_event_app/pages/Login/login_page.dart';
import 'package:my_event_app/providers/User/user.dart';
import 'package:provider/provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => UserModel()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
// is not restarted.
primarySwatch: Colors.blue,
),
home: const Wrapper(),
),
);
}
}
class Wrapper extends StatelessWidget {
const Wrapper({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.userChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
User? user = snapshot.data;
if (user == null) {
return const LoginPage();
}
return StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.snapshots(),
builder: (context, userSnapshot) {
if (userSnapshot.hasData) {
Provider.of<UserModel>(context, listen: true)
.fromJson(userSnapshot.data!.data());
return const HomePage();
}
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
});
} else {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
},
);
}
}
And this is the home page:
import 'package:flutter/material.dart';
import 'package:my_event_app/http/auth/user/sign_out.dart';
import 'package:my_event_app/pages/Create_Event/create_event_page.dart';
import 'package:my_event_app/pages/Onboarding/onboarding_page.dart';
import 'package:my_event_app/providers/User/user.dart';
import 'package:my_event_app/widgets/event_card_widget.dart';
import 'package:provider/provider.dart';
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Consumer<UserModel>(builder: (context, user, child) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: const Icon(Icons.help_outline, color: Colors.black, size: 30),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return const OnboardingPage();
}));
},
),
actions: [
IconButton(
icon: const Icon(
Icons.arrow_forward_ios_sharp,
color: Colors.black,
),
onPressed: () {
signOut();
},
),
],
elevation: 0,
backgroundColor: Colors.white,
),
backgroundColor: Colors.white,
body: SingleChildScrollView(
child: Container(
color: Colors.white,
padding: const EdgeInsets.all(16),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const CircleAvatar(
radius: 25,
backgroundImage: NetworkImage(
"https://cdnnmundo1.img.sputniknews.com/img/07e5/09/13/1116212032_100:0:1273:1173_1920x0_80_0_0_efb734331af13dfe11ff6d43293c60e2.png"),
),
Container(
height: 50,
width: 50,
decoration: BoxDecoration(
color: Colors.orange[400],
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 3),
),
],
),
child: Center(
child: IconButton(
color: Colors.white,
onPressed: () {
// Navigate to add event widget
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return const CreateEventPage();
}));
},
icon: const Icon(Icons.add),
),
),
),
],
),
const SizedBox(height: 32),
SizedBox(
width: double.infinity,
child: Text('Welcome, ${user.name}',
style: const TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
fontFamily: "Roboto")),
),
const SizedBox(height: 32),
Container(
padding: const EdgeInsets.all(16),
height: 100,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 3),
),
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Stack(alignment: Alignment.center, children: [
SizedBox(
height: 45,
width: 45,
child: CircularProgressIndicator(
valueColor:
AlwaysStoppedAnimation(Colors.orange[400]),
value: 14 / 20,
semanticsValue: "14/20",
color: Colors.black,
),
),
const Text("78%",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
fontFamily: "Roboto")),
]),
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text("Weekly progress",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
)),
Text("14/20 tasks completed"),
],
),
const Icon(Icons.bar_chart),
],
),
),
Container(
margin: const EdgeInsets.symmetric(vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: const [
Text("You have 5 tasks for today",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
)),
Icon(Icons.calendar_today_outlined)
],
),
),
_renderEvents(user),
],
),
),
),
);
});
}
}
Column _renderEvents(UserModel user) {
return Column(
children: [
for (var event in user.events)
EventCard(
eventId: event,
),
],
);
}
And here's the provider:
import 'package:flutter/material.dart';
class UserModel extends ChangeNotifier {
String _name = '';
String _surnames = '';
String _uid = '';
String _email = '';
List<dynamic> _events = [];
String get name => _name;
String get surnames => _surnames;
String get uid => _uid;
String get email => _email;
List<dynamic> get events => _events;
UserModel();
set name(String value) {
_name = value;
notifyListeners();
}
set surnames(String value) {
_surnames = value;
notifyListeners();
}
set uid(String value) {
_uid = value;
notifyListeners();
}
set email(String value) {
_email = value;
notifyListeners();
}
set events(List<dynamic> value) {
_events = value;
notifyListeners();
}
void addEvent(String event) {
_events.add(event);
notifyListeners();
}
void removeEvent(String event) {
_events.remove(event);
notifyListeners();
}
void updateUser(String name, String uid) {
name = name;
uid = uid;
notifyListeners();
}
void clearUser() {
_name = '';
_uid = '';
notifyListeners();
}
Map<String, dynamic> toJson() {
return {
'name': _name,
'surnames': _surnames,
'uid': _uid,
'email': _email,
'events': _events
};
}
fromJson(Object? json) {
try {
Map<dynamic, dynamic> map = json as Map<dynamic, dynamic>;
_name = map['name'];
_surnames = map['surnames'];
_uid = map['uid'];
_email = map['email'];
_events = map['events'];
} catch (e) {
print(e);
}
}
}
```
As you can see i use Consumer in order to read data and i have a change notifier in the begginig, but it won't re render and show for example new name if i change it in fireabase.
Thank you so much!
You are using fromJson method to update values in UserModel, but it does not call notifyListeners. Add notifyListeners(); to the end of this method:
fromJson(Object? json) {
try {
Map<dynamic, dynamic> map = json as Map<dynamic, dynamic>;
_name = map['name'];
_surnames = map['surnames'];
_uid = map['uid'];
_email = map['email'];
_events = map['events'];
notifyListeners(); // add this
} catch (e) {
print(e);
}
}
Also some other things:
Consider declaring class UserModel with ChangeNotifier instead of class UserModel extends ChangeNotifier.
fromJson methods usually are acting as factory methods, meaning these return a new instance, and don't set members in an existing instance.
Instead of Provider.of<UserModel>(context, listen: true).fromJson(userSnapshot.data!.data()); you can try: context.read<UserModel>().fromJson(userSnapshot.data!.data());. Here you don't really need listening, you just want to find the provider and call fromJson. Your Consumer is the one which is listening to the changes accordingly.
I am trying to display a list of cards from firestore on a page. This list is called from another page. If i manually set an integer value, the product displays the cards from that value, but if i do not set an int value, i get an error. How do i get the index of the card without using an itembuilder. Here is the code.
child: Center(
child: InkWell(
onTap: () async {
await productProvider
.loadProductsByCategory(
categoryName: categoryProvider
.categories[0].category);
changeScreen(
context,
CategoryScreen(
categoryModel:
categoryProvider.categories[0],
));
},
child: Text(widget.product.category),
)),
This is where i am having a problem. Just using [0] or any integer works. But i want to call the index, so that once i click on the category, it loads the particular one and all it's products. I have attached the full code.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class NewProductScreen extends StatefulWidget {
final ProductModel product;
const NewProductScreen({Key key, this.product}) : super(key: key);
#override
_NewProductScreenState createState() => _NewProductScreenState();
}
class _NewProductScreenState extends State<NewProductScreen> {
#override
Widget build(BuildContext context) {
final productProvider = Provider.of<ProductProvider>(context);
final categoryProvider = Provider.of<CategoryProvider>(context);
return Scaffold(
body: CustomScrollView(slivers: <Widget>[
SliverToBoxAdapter(
child: Column(
children: <Widget>[
Container(
margin:
EdgeInsets.only(top: 5, bottom: 5, right: 2),
width: MediaQuery
.of(context)
.size
.width / 3,
height: MediaQuery
.of(context)
.size
.height / 30,
child: Center(
child: InkWell(
onTap: () async {
await productProvider.loadProductsByCategory(
categoryName: categoryProvider
.categories[0].category);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
CategoryScreen(
categoryModel: categoryProvider
.categories[0],
)));
},
child: Text(widget.product.category),
)),
),
],
),
)
]));
}
}
class CategoryScreen extends StatefulWidget {
final CategoryModel categoryModel;
const CategoryScreen({Key key, this.categoryModel}) : super(key: key);
#override
_CategoryScreenState createState() => _CategoryScreenState();
}
class _CategoryScreenState extends State<CategoryScreen> {
#override
Widget build(BuildContext context) {
final productProvider = Provider.of<ProductProvider>(context);
return Scaffold(
appBar: AppBar(
title: Text("Category Filter"),
),
body: SafeArea(
child: ListView(
children: <Widget>[
Container(
height: MediaQuery
.of(context)
.size
.height / 10,
decoration: BoxDecoration(
color: Colors.orange.withOpacity(0.5),
image: DecorationImage(
image: NetworkImage(widget.categoryModel.icon))),
child: Center(
child: Text(
widget.categoryModel.category,
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 50,
),
),
),
),
Column(
children: productProvider.productsByCategory
.map((e) =>
GestureDetector(
onTap: () {
Navigator
.push(context, MaterialPageRoute(builder: (context)
=>
NewProductScreen(
product: e,
)
);
},
child: CategoryProductCard(
product: e,
),
))
.toList(),
)
],
)),
);
}
}
//Category ProductCard
class CategoryProductCard extends StatefulWidget {
final ProductModel product;
const CategoryProductCard({Key key, this.product}) : super(key: key);
#override
_CategoryProductCardState createState() => _CategoryProductCardState();
}
class _CategoryProductCardState extends State<CategoryProductCard> {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 4, right: 4, top: 4, bottom: 10),
child: Container(
height: 110,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.grey[300],
offset: Offset(-2, -1),
blurRadius: 5),
]),
// height: 160,
child: Row(
children: <Widget>[
Container(
width: 140,
height: 120,
child: ClipRRect(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(20),
topLeft: Radius.circular(20),
),
child: Image.network(
widget.product.images[0],
fit: BoxFit.fill,
),
),
),
Expanded(
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(widget.product.title)),
Padding(
padding: EdgeInsets.all(8),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.grey[300],
offset: Offset(1, 1),
blurRadius: 4),
]),
),
)
],
),
SizedBox(
height: 25,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Text(
"\N${widget.product.price}",
),
),
],
),
],
),
)
],
),
),
);
}
}
//Category Model
class CategoryModel {
static const ID = "id";
static const CATEGORY = "category";
static const ICON = "icon";
int _id;
String _category;
String _icon;
bool isSelected;
int get id => _id;
String get category => _category;
String get icon => _icon;
CategoryModel.fromSnapshot(DocumentSnapshot snapshot) {
_id = snapshot.data[ID];
_category = snapshot.data[CATEGORY];
_icon = snapshot.data[ICON];
}
}
// Category Provider
class CategoryProvider with ChangeNotifier {
CategoryServices _categoryServices = CategoryServices();
List<CategoryModel> categories = [];
CategoryModel categoryModel;
CategoryProvider.initialize() {
loadCategories();
}
loadCategories() async {
categories = await _categoryServices.getCategories();
notifyListeners();
}
loadSingleCategory({String singleCat}) async {
categoryModel =
await _categoryServices.getCategoryByName(category: singleCat);
notifyListeners();
}
}
// Category Service
class CategoryServices {
String collection = "categories";
Firestore _firestore = Firestore.instance;
Future<List<CategoryModel>> getCategories() async =>
_firestore.collection(collection).getDocuments().then((result) {
List<CategoryModel> categories = [];
for (DocumentSnapshot category in result.documents) {
categories.add(CategoryModel.fromSnapshot(category));
}
return categories;
});
Future<CategoryModel> getCategoryByName({String category}) =>
_firestore
.collection(collection)
.document(category.toString())
.get()
.then((doc) {
return CategoryModel.fromSnapshot(doc);
});
}
// Product Model
class ProductModel {
static const ID = "id";
static const TITLE = "Product Title";
static const Images = "Product Images";
static const PRICE = "Product Price";
static const CATEGORY = "Product Category";
// static const QUANTITY = "Product Quantity";
static const BRAND = "Product Brand";
String _id;
String _title;
List _images;
String _category;
String _brand;
// int _quantity;
double _price;
String get id => _id;
String get title => _title;
List get images => _images;
String get brand => _brand;
String get category => _category;
// int get quantity => _quantity;
double get price => _price.floorToDouble();
ProductModel.fromSnapshot(DocumentSnapshot snapshot) {
_id = snapshot.data[ID];
_brand = snapshot.data[BRAND];
_price = snapshot.data[PRICE];
_category = snapshot.data[CATEGORY];
_title = snapshot.data[TITLE];
_images = snapshot.data[Images];
}
}
class ProductProvider with ChangeNotifier {
ProductServices _productServices = ProductServices();
List<ProductModel> products = [];
List<ProductModel> productsByVendor = [];
List<ProductModel> productsByCategory = [];
ProductProvider.initialize() {
loadProducts();
}
loadProducts() async {
products = await _productServices.getProducts();
notifyListeners();
}
Future loadProductsByCategory({String categoryName}) async {
productsByCategory =
await _productServices.getProductsByCategory(category: categoryName);
notifyListeners();
}
}
class ProductServices {
String collection = "appProducts";
Firestore _firestore = Firestore.instance;
Future<List<ProductModel>> getProducts() async =>
_firestore.collection(collection).getDocuments().then((result) {
List<ProductModel> products = [];
for (DocumentSnapshot product in result.documents) {
products.add(ProductModel.fromSnapshot(product));
}
return products;
});
Future<List<ProductModel>> getProductsByCategory({String category}) async =>
_firestore
.collection(collection)
.where("Product Category", isEqualTo: category)
.getDocuments()
.then((result) {
List<ProductModel> products = [];
for (DocumentSnapshot product in result.documents) {
products.add(ProductModel.fromSnapshot(product));
}
return products;
});
}
You can try below way
final List uniqueList = Set.from(myList).toList();
uniqueList.map((val) {
String idx = uniqueList.indexOf(val);
return something;
}
For Example :
#override
Widget build(BuildContext context) {
var uniqueList = Set.from(['A','B','C','D','E']).toList();
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
children: uniqueList
.map((e) =>
GestureDetector(
onTap: () {
print("--index--${uniqueList.indexOf(e)}");
// Navigator
// .push(context, MaterialPageRoute(builder: (context)
// =>
// NewProductScreen(
// product: e,
// )
// );
},
child: Text('value :$e - index:${uniqueList.indexOf(e)}'),
))
.toList(),
)
),
);
}
Here, You should replace ['A','B','C','D','E'] list to you productProvider.productsByCategory
Output