The method '[ ]' was called on null - firebase

I am running my app and getting the error:
"NoSuchMethodError: The Method '[ ]' was called on null. Receiver: null. Tried calling: ."
This is also happening for "photourl" and "total questions", all three fields that I have in my firestore database.
This error is occurring after I implemented provider, so I am not sure if this is a result of this.
My code is below:
void main() {
runApp(
ChangeNotifierProvider(
builder: (context) => UserModel(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Profile Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Profile'),
);
}
}
class User {
final int name;
final DocumentReference reference;
User.fromMap(Map<String, dynamic> map, {this.reference})
: name = map['name'];
User.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
}
class Photo {
final int photourl;
final DocumentReference reference;
Photo.fromMap(Map<String, dynamic> map, {this.reference})
: photourl = map['photourl'];
Photo.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
}
class Questions {
final int totalquestions;
final DocumentReference reference;
Questions.fromMap(Map<String, dynamic> map, {this.reference})
: totalquestions = map['totalquestions'];
Questions.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
final _width = MediaQuery.of(context).size.width;
final _height = MediaQuery.of(context).size.height;
return Consumer<UserModel>(builder: (context, userModel, child) {
return StreamBuilder<DocumentSnapshot>(
stream: Firestore.instance.collection(userModel.uid)
.document(userModel.uid).snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Stack(
children: <Widget>[
new Container(
color: Colors.blue,
),
new Image.network(
snapshot.data['photourl'].toString(),
fit: BoxFit.fill,
),
new BackdropFilter(
filter: new ui.ImageFilter.blur(
sigmaX: 6.0,
sigmaY: 6.0,
),
child: new Container(
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.9),
borderRadius: BorderRadius.all(Radius.circular(50.0)),
),
)),
new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
centerTitle: false,
elevation: 0.0,
backgroundColor: Colors.transparent,
),
drawer: new Drawer(
child: new Container(),
),
backgroundColor: Colors.transparent,
body: new Center(
child: new Column(
children: <Widget>[
new SizedBox(
height: _height / 12,
),
new CircleAvatar(
radius: _width < _height ? _width / 4 : _height / 4,
backgroundImage: NetworkImage(snapshot.data['photourl']),
),
new SizedBox(
height: _height / 25.0,
),
new Text(
snapshot.data['name'],
style: new TextStyle(fontWeight: FontWeight.bold, fontSize: _width / 15, color: Colors.white),
),
new Padding(
padding: new EdgeInsets.only(top: _height / 30, left: _width / 8, right: _width / 8),
),
new Divider(
height: _height / 15,
color: Colors.white,
),
new Row(
children: <Widget>[
rowCell(snapshot.data['totalquestions'], 'Answers'),
rowCell('£ ${int.parse(snapshot.data['totalquestions']) * 2}', 'Earned'),
],
),
new Divider(height: _height / 15, color: Colors.white),
],
),
),
),
],
);
} else {
return CircularProgressIndicator();
}
},
);
});
}

The error simply states that the operator [] is called on a variable that does not contain a Map object and is rather null. I would suggest you to place a check for null or provide a default value for map using ?? everytime before you access a property of map.

This is just speculation but I think you made a mistake when specifying your stream. The collection you query is userModel.uid. Is that how your collection is named?

You should always check if the image is null when you work with NetworkImage since its an async task. The code could look like this:
backgroundImage: snapshot.data['photourl'] != null ?NetworkImage(snapshot.data['photourl']) : Container(),

Related

Unable to retrieve data from Firebase Realtime Database in Flutter Application [duplicate]

I am trying to create a simple app that adds posts to a firebase realtime database and shows them on a listview in flutter, but when I "post" it to the database, on the firebase console, the data value constantly appears as null. does anyone know what is wrong?
Here is a bit of my code -
post.dart
class Post {
Image coverImg;
File audioFile;
String body;
String author;
Set usersLiked = {};
DatabaseReference _id;
Post(this.body, this.author);
void likePost(User user) {
if (this.usersLiked.contains(user.uid)) {
this.usersLiked.remove(user.uid);
} else {
this.usersLiked.add(user.uid);
}
}
void update() {
updatePost(this, this._id);
}
void setId(DatabaseReference id) {
this._id = id;
}
Map<String, dynamic> toJson() {
return {
'body': this.body,
'author': this.author,
'usersLiked': this.usersLiked.toList()
};
}
}
Post createPost(record) {
Map<String, dynamic> attributes = {
'author': '',
'usersLiked': [],
'body': ''
};
record.forEach((key, value) => {attributes[key] = value});
Post post = new Post(attributes['body'], attributes['author']);
post.usersLiked = new Set.from(attributes['usersLiked']);
return post;
}
class PostList extends StatefulWidget {
final List<Post> listItems;
final User user;
PostList(this.listItems, this.user);
#override
_PostListState createState() => _PostListState();
}
class _PostListState extends State<PostList> {
void like(Function callBack) {
this.setState(() {
callBack();
});
}
#override
void initState() {
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: this.widget.listItems.length,
itemBuilder: (context, index) {
var post = this.widget.listItems[index];
return Card(
elevation: 7.0,
color: Colors.white,
child: Column(
children: [
Row(
children: [
IconButton(
icon: Icon(
Icons.play_circle_outline,
color: Colors.black,
size: 30,
),
onPressed: () {},
),
Expanded(
child: ListTile(
title: Text(post.body,
style: TextStyle(
fontWeight: FontWeight.w600,
color: Colors.black)),
subtitle: Text(post.author,
style: TextStyle(color: Colors.black)),
),
),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(post.usersLiked.length.toString(),
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.w600)),
IconButton(
icon: Icon(
Icons.thumb_up,
color: post.usersLiked.contains(widget.user.uid)
? Colors.blue
: Colors.black,
),
onPressed: () =>
this.like(() => post.likePost(widget.user)),
)
],
))
],
)
],
));
},
);
}
}
database.dart
final databaseReference = FirebaseDatabase.instance.reference();
DatabaseReference savePost(Post post) {
var id = databaseReference.child('posts/').push();
id.set(post.toJson());
return id;
}
void updatePost(Post post, DatabaseReference id) {
id.update(post.toJson());
}
Future<List<Post>> getAllPosts() async {
DataSnapshot dataSnapshot = await databaseReference.child('posts/').once();
List<Post> posts = [];
if (dataSnapshot.value != null) {
dataSnapshot.value.forEach((key, value) {
Post post = createPost(value);
post.setId(databaseReference.child('posts/' + key));
posts.add(post);
});
}
return posts;
}
home.dart
class UserInfoScreen extends StatefulWidget {
const UserInfoScreen({Key key, User user})
: _user = user,
super(key: key);
final User _user;
#override
_UserInfoScreenState createState() => _UserInfoScreenState();
}
class _UserInfoScreenState extends State<UserInfoScreen> {
User _user;
bool _isSigningOut = false;
List<Post> posts = [];
void newPost(String text) {
var post = new Post(text, widget._user.displayName);
post.setId(savePost(post));
this.setState(() {
posts.add(post);
});
}
void updatePosts() {
getAllPosts().then((posts) => {
this.setState(() {
this.posts = posts;
})
});
}
Route _routeToSignInScreen() {
return PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => SignInScreen(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
var begin = Offset(-1.0, 0.0);
var end = Offset.zero;
var curve = Curves.ease;
var tween =
Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
);
}
#override
void initState() {
_user = widget._user;
// HOME PAGE
pageList.add(Scaffold(
backgroundColor: Colors.white,
body: Column(children: [
Expanded(
child: PostList(this.posts, _user),
),
]),
));
pageList.add(Test1());
pageList.add(Scaffold(
backgroundColor: Colors.white,
body: Column(
children: [
TextInputWidget(this.newPost, "Title:"),
],
),
));
pageList.add(Test3());
// ACCOUNT PAGE
pageList.add(
Scaffold(
backgroundColor: Colors.white,
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(),
_user.photoURL != null
? ClipOval(
child: Material(
color: Colors.grey.withOpacity(0.3),
child: Image.network(
_user.photoURL,
fit: BoxFit.fitHeight,
),
),
)
: ClipOval(
child: Material(
color: Colors.grey.withOpacity(0.3),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Icon(
Icons.person,
size: 60,
color: Colors.black,
),
),
),
),
SizedBox(height: 16.0),
Text(
'${_user.displayName}',
style: TextStyle(
color: Colors.black,
fontSize: 26,
fontWeight: FontWeight.w700),
),
SizedBox(height: 8.0),
Text(
' ${_user.email} ',
style: TextStyle(
color: Colors.black,
fontSize: 20,
letterSpacing: 0.5,
),
),
SizedBox(height: 60.0),
_isSigningOut
? CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
)
: ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Colors.redAccent,
),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
onPressed: () async {
setState(() {
_isSigningOut = true;
});
await Authentication.signOut(context: context);
setState(() {
_isSigningOut = false;
});
Navigator.of(context)
.pushReplacement(_routeToSignInScreen());
},
child: Padding(
padding: EdgeInsets.only(top: 8.0, bottom: 8.0),
child: Text(
'Logout',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Colors.white,
),
),
),
),
],
),
),
);
super.initState();
updatePosts();
}
#override
List<Widget> pageList = List<Widget>();
int _currentIndex = 0;
void onTabTapped(int index) {
setState(() {
_currentIndex = index;
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0.0,
backgroundColor: Colors.white,
title: Text(
'SoundFX',
style: TextStyle(color: Colors.black),
),
centerTitle: true,
),
body: IndexedStack(
index: _currentIndex,
children: pageList,
),
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.grey,
selectedItemColor: Colors.blue,
backgroundColor: Colors.white,
elevation: 19.0,
onTap: onTabTapped,
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(
icon: new Icon(Icons.home),
title: Container(
height: 1.0,
)),
BottomNavigationBarItem(
icon: new Icon(Icons.search),
title: Container(
height: 1.0,
)),
BottomNavigationBarItem(
icon: new Icon(
Icons.add_circle,
size: 40,
),
title: Container(
height: 1.0,
)),
BottomNavigationBarItem(
icon: new Icon(Icons.videocam),
title: Container(
height: 1.0,
)),
BottomNavigationBarItem(
icon: Icon(Icons.account_circle),
title: Container(
height: 1.0,
)),
],
),
);
}
}
class Test1 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.green,
);
}
}
class Test2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.blue,
);
}
}
class Test3 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
color: Colors.yellow,
);
}
}
text-input.dart
class TextInputWidget extends StatefulWidget {
final Function(String) callback;
String label;
TextInputWidget(this.callback, this.label);
#override
_TextInputWidgetState createState() => _TextInputWidgetState();
}
class _TextInputWidgetState extends State<TextInputWidget> {
final controller = TextEditingController();
#override
void dispose() {
super.dispose();
controller.dispose();
}
void click() {
widget.callback(controller.text);
FocusScope.of(context).unfocus();
controller.clear();
}
void pickFiles() async {
FilePickerResult result =
await FilePicker.platform.pickFiles(type: FileType.any);
File file = File(result.files.first.path);
print(file);
}
#override
Widget build(BuildContext context) {
return Container(
height: 400,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: Container(
padding: EdgeInsets.all(20.0),
child: TextField(
controller: this.controller,
decoration: InputDecoration(
labelText: this.widget.label,
))),
),
Container(
child: ElevatedButton(onPressed: this.click, child: Text('Post')),
),
Container(
child:
ElevatedButton(onPressed: this.pickFiles, child: Text('test')),
)
],
),
);
}
}
PS: Also, note that some of the functions and classes are unused or basically useless, and some of the names are very stupid, but please do not comment on those if they are not related to the question, as those are just for some tests for features for me to add into the application.
Also, note that i followed this tutorial - https://www.youtube.com/playlist?list=PLzMcBGfZo4-knQWGK2IC49Q_5AnQrFpzv
firebaser here
Most likely the Firebase SDK is unable to read the URL of the database from its config file, because your database is hosted outside of the default (US) region.
If that is indeed the cause, you should specify the database URL in your code. By specifying it in your code, the SDK should able to connect to the database instance no matter what region it is hosted in.
This mean you should get your database with
FirebaseDatabase("your database URL").reference()
// Or
FirebaseDatabase(databaseURL: "your database URL").reference()
instead of
FirebaseDatabase.instance.reference()
This is documented here:
To get a reference to a database other than a us-central1 default dstabase, you must pass the database URL to getInstance() (or Kotlin+KTX database()) . For a us-central1 default database, you can call getInstance() (or database) without arguments.
So while this is documented, it is incredibly easy to overlook. I'm checking with our team if there's something we can do to make this configuration problem more obvious.

flutter firestore image: How to display image through url which is in firebase firestore?

I am getting the images url in console and it is printing in it, but I am not able to display the image inside the container. This is the complete code with Dot indicators and delete button. This code works fine as I'm able to display the image if I get them direct through cloud storage but not able to get it through firestore which is stored in image collection in array with index uploaded as map. But url gets print in console as I have mentioned in code below. pls check it out. PS: second and third page may or may be not related here with query but it might help someone who are trying to get dot indicators or delete button or full image view as this code works fine if I get the images direct through cloud storage.Thanks for your time.
class PortfolioGallarySubPageTWO extends StatefulWidget{
int currentIndex;
List<String> imageList;
PortfolioGallarySubPageTWO({Key key,#required this.imageList,#required this.currentIndex})
:super(key:key);
#override
_PortfolioGallarySubPageTWO createState() => _PortfolioGallarySubPageTWO();
}
class _PortfolioGallarySubPageTWO extends State<PortfolioGallarySubPageTWO>
with SingleTickerProviderStateMixin {
final GlobalKey<FormState> formKey = new GlobalKey<FormState>();
final GlobalKey<ScaffoldState> key = new GlobalKey<ScaffoldState>();
final _stoarge = FirebaseStorage.instance;
final CollectionReference _reference = FirebaseFirestore.instance.collection(
"users");
final auth = FirebaseAuth.instance;
final FirebaseFirestore fb = FirebaseFirestore.instance;
List<File> _images = [];
List<String> imageList = [];
int currentIndex;
#override
void initState() {
super.initState();
_getImagesOK();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: key,
extendBodyBehindAppBar: true,
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.transparent,
actions: [
ElevatedButton(
child: Text("DONE", style: TextStyle(fontSize: 15)),
onPressed: () {
_uploadImages();
},
style: ElevatedButton.styleFrom(
padding: EdgeInsets.fromLTRB(25.0, 15.0, 25.0, 10.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(30.0))),
),
],
),
body: Container(
width: _maxScreenWidth,
child: SafeArea(
child: Form(
key: formKey,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 160.0,
padding: EdgeInsets.symmetric(vertical: 15.0,horizontal: 15.0),
child: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
SliverPadding(
padding: const EdgeInsets.all(10),
sliver:
_buildContentTWO(imageList),
)
],
),
),
],
),
),
),
),
),
);
}
_getImagesOK() async {
DocumentSnapshot firebaseDoc = await FirebaseFirestore.instance.collection(
"users").doc(auth.currentUser.uid).get();
List imageList = firebaseDoc.data()['images'];
print(imageList[0]);
String _u = await imageList.toString();
imageList.add(_u);
setState(() {
print(imageList.length);
print(imageList);
});
}
SliverGrid _buildContentTWO(List<String> imageList) {
return SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return PortfolioGalleryImageWidgetTWO(imageList: imageList[index],
onImageTap: () =>
Navigator.push(context,
_createGalleryDetail(imageList, index),
),
);
},
childCount: imageList.length,
),
);
}
MaterialPageRoute _createGalleryDetail(List<String> imageList, int index) {
return MaterialPageRoute(builder: (context) =>
PortfolioGalleryDetailPageTWO(
imageList: imageList,
currentIndex: index,
),
);
}
}
The second page: The page is for DOT indicators and to get index and displayed on image
class PortfolioGalleryDetailPageTWO extends StatefulWidget{
final List<String> imageList;
final int currentIndex;
PortfolioGalleryDetailPageTWO({Key key, #required this.imageList,#required this.currentIndex})
: super(key: key);
#override
_PortfolioGalleryDetailPageTWO createState() => _PortfolioGalleryDetailPageTWO();
}
class _PortfolioGalleryDetailPageTWO extends State<PortfolioGalleryDetailPageTWO>{
int _currentIndex;
PageController _pageController;
#override
void initState(){
super.initState();
_currentIndex = widget.currentIndex;
_pageController = PageController(initialPage: _currentIndex);
}
final auth = FirebaseAuth.instance;
#override
Widget build (BuildContext context){
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(backgroundColor: Colors.black,),
body: _buildContent(),
);
}
Widget _buildContent(){
return Stack(
children: [
_buildPhotoViewGallery(),
_buildIndicator(),
_deleteImage(),
],
);
}
Widget _deleteImage(){
return Positioned(
right: -2,
top: -9,
child: new Deletebtn(),
);
}
Widget _buildIndicator(){
return Positioned(
bottom: 2.0,
left: 2.0,
right: 2.0,
// child: _buildDot(),
child: _buildDottedIndicator(),
);
}
Row _buildDottedIndicator(){
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: widget.imageList.map<Widget>((String imageList) => _buildDot(imageList)).toList(),
);
}
Container _buildDot(String imageList){
return Container(
width: 5,
height: 5,
margin: EdgeInsets.symmetric(vertical: 10.0,horizontal: 2.0),
decoration: new BoxDecoration(
color: _currentIndex == widget.imageList.indexOf(imageList) ? Colors.red : Colors.white,
borderRadius: new BorderRadius.circular(2.5),
boxShadow: [
new BoxShadow(
color: Colors.red,
blurRadius: 10.0,
spreadRadius: 0.0,
offset: const Offset(0.0, 1.0))
]),
);
}
PhotoViewGallery _buildPhotoViewGallery(){
return PhotoViewGallery.builder(
itemCount: widget.imageList.length,
builder: (BuildContext context,int index){
return PhotoViewGalleryPageOptions(
imageProvider: NetworkImage(widget.imageList[index]),
minScale: PhotoViewComputedScale.contained * 0.8,
maxScale: PhotoViewComputedScale.covered * 1.8,
);
},
scrollPhysics: const BouncingScrollPhysics(),
pageController: _pageController,
onPageChanged: (int index){
setState(() {
_currentIndex = index;
});
},
scrollDirection: Axis.horizontal,
);
}
}
class Deletebtn extends StatelessWidget{
final auth = FirebaseAuth.instance;
#override
Widget build(BuildContext context ) {
return new Padding(padding: const EdgeInsets.all(10.0),
child: IconButton(
icon: Icon(Icons.delete_forever_outlined,
color: const Color(0xFF227770),
size: 35,
),
onPressed: () async {
var val =[];
var userCollection = FirebaseFirestore.instance.collection("users").doc(auth.currentUser.uid).update({ "images" : FieldValue.arrayRemove(val)}).then((_) {
print("deleted");
});
},
),
);
}
}
The third page: This is for the how image look inside the container and on image tap it gives full screen image view.
class PortfolioGalleryImageWidgetTWO extends StatelessWidget{
final String imageList;
final VoidCallback onImageTap;
const PortfolioGalleryImageWidgetTWO({Key key,#required this.imageList, #required this.onImageTap})
:super(key: key);
#override
Widget build(BuildContext context) {
return Container(
decoration:BoxDecoration(
boxShadow:[
BoxShadow(
color: Colors.black,
offset: Offset(2, 2),
spreadRadius: 2,
blurRadius: 5,
),
],
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: ClipRRect(
borderRadius:BorderRadius.all(Radius.circular(10)),
child: Material(
color: Colors.transparent,
child: Ink.image(image: NetworkImage(imageList),
fit: BoxFit.cover,
child: InkWell(onTap: onImageTap),
),
),
),
);
}
}
Try this first:
_getImagesOK() async {
DocumentSnapshot firebaseDoc = await FirebaseFirestore.instance.collection(
"users").doc(auth.currentUser.uid).get();
List<String> imageListTemp =[];
for(var item in firebaseDoc.data()['images']){
imageListTemp.add(item);
}
setState(() {
imageList=imageListTemp;
print(imageList.length);
print(imageList);
});
}

Flutter Web and Firebase List<String> and List <dynamic> error using Provider

I've built a web app using Flutter. Built and deployed with no issues a couple of months ago. Have jumped back into the code today without updating any code and now am getting the following error:
Error:Expected a value of type 'List<String>', but got one of type 'List<dynamic>'
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following JSNoSuchMethodError was thrown building NewHomeScreen(dirty, dependencies:
[_EffectiveTickerMode, _InheritedProviderScope<List<ContentModel>>, MediaQuery], state:
_NewHomeScreenState#295f1(tickers: tracking 2 tickers)):
NoSuchMethodError: invalid member on null: 'length'
This is where and how I get data from Firebase:
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
// This widget is the root of your application.
#override
_MyAppState createState() => _MyAppState();
}
#override
void initState() {}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
final linksCollection = Firestore.instance.collection('links');
final contentCollection = Firestore.instance.collection('content');
final contentObjects = contentCollection.snapshots().map((snapshot) {
return snapshot.documents
.map((doc) => ContentModel.fromDocument(doc))
.toList();
});
return MultiProvider(
providers: [
StreamProvider<List<ContentModel>>(
create: (_) => contentObjects,
initialData: [],
catchError: (BuildContext context, e) {
print("Error:$e");
return null;
},
),
Provider<CollectionReference>(create: (_) => linksCollection),
],
child: MaterialApp(
title: 'My App',
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.blue, fontFamily: 'IBM_Plex'),
initialRoute: '/',
routes: {'/': (context) => NewHomeScreen()},
),
);
}
}
I then consume this data throughout the app by accessing it using Provider like so:
class NewHomeScreen extends StatefulWidget {
#override
_NewHomeScreenState createState() => _NewHomeScreenState();
}
class _NewHomeScreenState extends State<NewHomeScreen>
with TickerProviderStateMixin {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
final contentObjects = Provider.of<List<ContentModel>>(context);
List<ContentModel> expertList = [];
for (var data in contentObjects) {
if(data.topic == 'expert') {
expertList.add(data);
}
}
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
leading: Container(
child: Padding(
padding: EdgeInsets.only(left: 10.0),
child: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => NewHomeScreen(),
),
);
},
),
)
),
backgroundColor: appBarColor,
expandedHeight: 50.0,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Align(
alignment: Alignment.center,
),
centerTitle: true,
stretchModes: [
StretchMode.blurBackground,
StretchMode.zoomBackground
],
background: Image.network(
'https://www.image.com',
fit: BoxFit.cover,
),
),
actions: <Widget>[
InkResponse(
onTap: () {
Navigator.push(
context,
SlideRightRoute(
page: SearchScreen(),
),
);
},
child: new Padding(
padding: const EdgeInsets.all(12.0),
child: Icon(
Icons.search,
size: 26.0,
color: Colors.white,
),
),
),
],
),
SliverToBoxAdapter(
child: Column(
children: <Widget>[
FadeIn(1.00, Center(child: HeaderWidget())),
FadeIn(2.33, Center(child: HashtagRow())),
SizedBox(
height: 20,
),
SizedBox(height: 50),
FadeIn(
2.66,
SectionContainer(
sectionTitle: "Expertise in focus",
child: Padding(
padding: EdgeInsets.only(top: 13, bottom: 13),
child: Container(
height: 450,
child: ListView.builder(
padding: EdgeInsets.only(left: 50, right: 50),
scrollDirection: Axis.horizontal,
itemCount: expertList.length,
itemBuilder: (ctx, index) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ExpertDetailsScreen(
contentModel: expertList[index],
),
),
);
},
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(
left: 15.0,
right: 15.0,
),
child: Hero(
tag: expertList[index].title.toString(),
child: Align(
alignment: Alignment.centerLeft,
child: CircleAvatar(
radius: 150.0,
backgroundImage: NetworkImage(
expertList[index].imglink),
backgroundColor: Colors.transparent,
),
),
),
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
),
child: Padding(
padding: const EdgeInsets.all(10),
child: Center(
child: Text(
expertList[index].tags[1],
textAlign: TextAlign.center,
style: forumNameTextStyleTwo,
),
),
),
),
SizedBox(height: 3),
Text(
expertList[index].title,
textAlign: TextAlign.center,
style: labelTextStyle,
),
],
),
);
},
),
),
),
),
),
SizedBox(height: 50)
],
),
)
],
),
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
Navigator.push(
context,
ScaleRoute(
page: AddResource(),
),
);
},
label: Text('Suggest a resource'),
icon: Icon(Icons.add),
backgroundColor: myColor,
),
);
}
void htmlOpenLink(String s) {
html.window.open(s, '_blank');
}
}
class SlideRightRoute extends PageRouteBuilder {
final Widget page;
SlideRightRoute({this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) =>
page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
SlideTransition(
position: Tween<Offset>(
begin: const Offset(-1, 0),
end: Offset.zero,
).animate(
CurvedAnimation(
parent: animation,
curve: Curves.fastOutSlowIn,
),
),
child: child,
),
);
}
class ScaleRoute extends PageRouteBuilder {
final Widget page;
ScaleRoute({this.page})
: super(
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) =>
page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
ScaleTransition(
scale: Tween<double>(
begin: 0.0,
end: 1.0,
).animate(
CurvedAnimation(
parent: animation,
curve: Curves.fastOutSlowIn,
),
),
child: child,
),
);
}
class MyCustomClipper extends CustomClipper<Path> {
final double distanceFromWall = 12;
final double controlPointDistanceFromWall = 2;
#override
Path getClip(Size size) {
final double height = size.height;
final double halfHeight = size.height * 0.5;
final double width = size.width;
Path clippedPath = Path();
clippedPath.moveTo(0, halfHeight);
clippedPath.lineTo(0, height - distanceFromWall);
clippedPath.quadraticBezierTo(0 + controlPointDistanceFromWall,
height - controlPointDistanceFromWall, 0 + distanceFromWall, height);
clippedPath.lineTo(width, height);
clippedPath.lineTo(width, 0 + 30.0);
clippedPath.quadraticBezierTo(width - 5, 0 + 5.0, width - 35, 0 + 15.0);
clippedPath.close();
return clippedPath;
}
#override
bool shouldReclip(CustomClipper<Path> oldClipper) {
return true;
}
}
class CustomShapeBorder extends ShapeBorder {
final double distanceFromWall = 12;
final double controlPointDistanceFromWall = 2;
#override
EdgeInsetsGeometry get dimensions => null;
#override
Path getInnerPath(Rect rect, {TextDirection textDirection}) {
return null;
}
#override
Path getOuterPath(Rect rect, {TextDirection textDirection}) {
return getClip(Size(220.0, 70.0));
}
#override
void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {}
#override
ShapeBorder scale(double t) {
return null;
}
Path getClip(Size size) {
Path clippedPath = Path();
clippedPath.moveTo(0 + distanceFromWall, 0);
clippedPath.quadraticBezierTo(0 + controlPointDistanceFromWall,
0 + controlPointDistanceFromWall, 0, 0 + distanceFromWall);
clippedPath.lineTo(0, size.height - distanceFromWall);
clippedPath.quadraticBezierTo(
0 + controlPointDistanceFromWall,
size.height - controlPointDistanceFromWall,
0 + distanceFromWall,
size.height);
clippedPath.lineTo(size.width - distanceFromWall, size.height);
clippedPath.quadraticBezierTo(
size.width - controlPointDistanceFromWall,
size.height - controlPointDistanceFromWall,
size.width,
size.height - distanceFromWall);
clippedPath.lineTo(size.width, size.height * 0.6);
clippedPath.quadraticBezierTo(
size.width - 1,
size.height * 0.6 - distanceFromWall,
size.width - distanceFromWall,
size.height * 0.6 - distanceFromWall - 3);
clippedPath.lineTo(0 + distanceFromWall + 6, 0);
clippedPath.close();
return clippedPath;
}
}
Here is the model class for the data:
class ContentModel {
String title;
String description;
String imglink;
int contentId;
List<String> tags;
List<String> focusAreas;
int likeCount;
String myIcon;
bool isNew;
String content;
String contentLink;
String appColor;
double positionVar;
String detailScreenLink;
String documentId;
String topic;
String hashtag;
ContentModel(
{this.title,
this.description,
this.imglink,
this.contentId,
this.tags,
this.likeCount,
this.myIcon,
this.isNew,
this.content,
this.contentLink,
this.appColor,
this.positionVar,
this.detailScreenLink,
this.documentId,
this.topic,
this.focusAreas,
this.hashtag});
Map<String, dynamic> toMap() {
return {
'title': title,
'description': description,
'imglink': imglink,
'contentId': contentId,
'tags': tags,
'likeCount': likeCount,
'isNew': isNew,
'content': content,
'contentLink': contentLink,
'appColor': appColor,
'positionVar': positionVar,
'detailScreenLink': detailScreenLink,
'documentId': documentId,
'topic': topic,
'focusAreas': focusAreas,
'hashtag': hashtag
};
}
static ContentModel fromDocument(DocumentSnapshot document) {
if (document == null || document.data == null) return null;
return ContentModel(
documentId: document.documentID,
imglink: document.data['imglink'],
title: document.data['title'],
description: document.data['description'],
likeCount: document.data['likeCount'],
tags: document.data['tags'],
isNew: document.data['isNew'],
content: document.data['content'],
contentLink: document.data['contentLink'],
appColor: document.data['appColor'],
positionVar: document.data['positionVar'],
detailScreenLink: document.data['detailScreenLink'],
topic: document.data['topic'],
focusAreas: document.data['focusAreas'],
hashtag: document.data['hashtag']);
}
Map toJson() => {
'title': title,
'description': description,
'imglink': imglink,
'contentId': contentId,
'tags': tags,
'likeCount': likeCount,
'isNew': isNew,
'content': content,
'contentLink': contentLink,
'appColor': appColor,
'positionVar': positionVar,
'detailScreenLink': detailScreenLink,
'documentId': documentId,
'topic': topic,
'focusAreas': focusAreas,
'hashtag': hashtag
};
}
Given
List<dynamic> dynamicList;
You can use
var stringList = List<String>.from(dlist);
to convert a List<dynamic> to List<String>
Therefore you need to fix your mode:
static ContentModel fromDocument(DocumentSnapshot document) {
if (document == null || document.data == null) return null;
return ContentModel(
documentId: document.documentID,
imglink: document.data['imglink'],
title: document.data['title'],
description: document.data['description'],
likeCount: document.data['likeCount'],
tags: List<String>.from(document.data['tags']),// to convert a List<dynamic> to List<String>
isNew: document.data['isNew'],
content: document.data['content'],
contentLink: document.data['contentLink'],
appColor: document.data['appColor'],
positionVar: document.data['positionVar'],
detailScreenLink: document.data['detailScreenLink'],
topic: document.data['topic'],
focusAreas: List<String>.from(document.data['focusAreas']), //to convert a List<dynamic> to List<String>
hashtag: document.data['hashtag']);}
I think the issue arises from the builder parameter, itemCount: expertList.length.
One possible case could be, that the expertList is not yet populated from the backend, when the widget build is being triggered. I'd suggest using a wait parameter to ensure the data has been populated before rendering the builder on screen. In my experience, I was able to achieve this functionality using a ModalProgressHud configured to my state.waiting boolean.
Another solution is to just add the null checks. Quick fix could be:
expertList.isNotEmpty ?
ListView.builder(
padding: EdgeInsets.only(left: 50, right: 50),
scrollDirection: Axis.horizontal,
itemCount: expertList.length, ... )
: Container();
This ensures that the ListView Builder is only added to the widget tree if already populated. Hence, bypasses the null issues.

How do i add cart page for my ecommerce app on flutter?

Im using firebase as a backend. I retrieved products from firebase using stream builder .I looked into providers but all the tutorials used providers for the products page too.Can i use provviders only for cart?How do i do it?Or is there any other way to implement cart page?
here's the code for the products page.
class Shop extends StatefulWidget {
final User currentUser;
final String prodId;
final String onwerId;
Shop({ this.currentUser,
this.prodId,
this.onwerId});
#override
_ShopState createState() => _ShopState( prodId: this.prodId,ownerId:this.onwerId);
}
class _ShopState extends State<Shop> {
final _firestore = Firestore.instance;
String postOrientation = "grid";
String shopOrientation = "grid";
bool isFollowing = false;
bool isLoading = false;
String uid="";
String prodId;
String ownerId;
Prod products;
_ShopState({
this.prodId, this.products,this.ownerId,
});
#override
Widget build(BuildContext context) {
return
Scaffold(
appBar: AppBar(backgroundColor: kSecondaryColor,
title: Text( 'Shop',
style: TextStyle(
fontFamily :"MajorMonoDisplay",
fontSize: 35.0 ,
color: Colors.white),),
iconTheme: new IconThemeData(color: kSecondaryColor),
),
backgroundColor: kPrimaryColor,
body:StreamBuilder(
stream: Firestore.instance.collectionGroup("userProducts").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
} else {
return new ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) {
DocumentSnapshot ds = snapshot.data.documents[index];
return new ShopItem(
shopmediaUrl: ds['shopmediaUrl'],
productname: ds['productname'],
price: ds['price'],
photoUrl: ds['photoUrl'],
username: ds['username'],
prodId: ds['prodId'],
userId: ds['userId'],
ownerId: ds['ownerId'],
);
}
);
}
}
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.black38,
onPressed: ()
async{ Navigator.push(context, MaterialPageRoute(builder: (context) =>Uploadecom(currentUser: currentUser, )));
},
child: Icon(Icons.add_box),
),
);
}
}
class ShopItem extends StatelessWidget {
final String username;
final String prodId;
final String ownerId;
final String photoUrl;
final String shopmediaUrl;
final String productname;
final String price;
final String userId;
ShopItem({
this.ownerId,
this.prodId,
this.shopmediaUrl,
this.username,
this.photoUrl,
this.price,
this.productname,
this.userId,
});
showProduct(context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductScreen(
prodId: prodId,
userId: ownerId,
),
),
);
}
#override
Widget build(BuildContext context) {
// configureshopmediaPreview(context);
return
//
Column( children: <Widget>[
Stack(
children: <Widget>[
Container(
child:Row(
children: <Widget>[
Expanded(
child: Container(
// width: 360,
height: 400.0,
child:AspectRatio(
aspectRatio: 16 / 9,
child:Container(
child: DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
shopmediaUrl),
fit: BoxFit.cover),
),
),
),
),
),
)],
),
),
Positioned(
bottom: 10,
left: 10,
child: Container(
height: 40,
width: 40,
child: cachedNetworkImage(photoUrl),
),
),
Positioned(
bottom: 20,
left: 60,
child: Container(
child: Text(username,style: TextStyle(color: Colors.white,fontWeight:FontWeight.bold),),
),
),
Container(
alignment: Alignment.bottomRight,
child: Container(
alignment: Alignment.bottomRight,
child: GFButton(
onPressed: () => showProduct(context) ,
text: "More",
icon: Icon(Icons.card_travel),
shape: GFButtonShape.pills,
),
),
),
],
),
Row(
children: <Widget>[
Container(
child: Text(productname,style: TextStyle(color: kText,fontSize: 30.0,fontWeight:FontWeight.bold),),
),
],
),
Row(
children: <Widget>[
Container( child: Text('₹',style: TextStyle(color: kText,)),
),
Container(
child: Text(price,style: TextStyle(color: kText,fontSize: 20.0,fontWeight:FontWeight.bold),),
),
],
),
Divider(color: kGrey,),
],
);
}
}
Yes, you can use provider(or any state management solution) for the cart page only, but you have to persist those cart products so when the user logs out or exit your app and returns he will find his cart products still available, for persisting I suggest creating a carts collection in your Firestore and for every user create a cart.

Trouble pulling data from firestore

database structure screenshotI am building a profile screen and am trying to pull each user's name from a database in firestore using streambuilder. However, my code does not seem to be pulling from the firestore database and I am still getting the same name (each time I run the code) that I had previously hard coded. Below is my code (there are no errors in the actual code). How can I modify my code so that it will actually read from the firestore database?
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'dart:ui' as ui;
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Profile Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Profile'),
);
}
}
class ProfileWidget extends StatelessWidget {
final String userId;
ProfileWidget(this.userId);
#override
Widget build(BuildContext context) {
return StreamBuilder<DocumentSnapshot>(
stream:
Firestore.instance.collection('users').document(userId).snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
debugPrint('the snapshot name is:'+ snapshot.data['name']);
User user = User.fromSnapshot(snapshot.data);
return Row(
children: <Widget>[Text(snapshot.data['name'].toString())]);
} else {
return CircularProgressIndicator();
}
});
}
}
class User {
final int name;
final DocumentReference reference;
User.fromMap(Map<String, dynamic> map, {this.reference}) : name = map['name'];
User.fromSnapshot(DocumentSnapshot snapshot)
: this.fromMap(snapshot.data, reference: snapshot.reference);
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
final _width = MediaQuery.of(context).size.width;
final _height = MediaQuery.of(context).size.height;
final String imgUrl =
'https://pixel.nymag.com/imgs/daily/selectall/2017/12/26/26-eric-schmidt.w700.h700.jpg';
return new Stack(
children: <Widget>[
new Container(
color: Colors.blue,
),
new Image.network(
imgUrl,
fit: BoxFit.fill,
),
new BackdropFilter(
filter: new ui.ImageFilter.blur(
sigmaX: 6.0,
sigmaY: 6.0,
),
child: new Container(
decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.9),
borderRadius: BorderRadius.all(Radius.circular(50.0)),
),
)),
new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
centerTitle: false,
elevation: 0.0,
backgroundColor: Colors.transparent,
),
drawer: new Drawer(
child: new Container(),
),
backgroundColor: Colors.transparent,
body: new Center(
child: new Column(
children: <Widget>[
new SizedBox(
height: _height / 12,
),
new CircleAvatar(
radius: _width < _height ? _width / 4 : _height / 4,
backgroundImage: NetworkImage(imgUrl),
),
new SizedBox(
height: _height / 25.0,
),
new Text(
'Eric Schmidt',
style: new TextStyle(
fontWeight: FontWeight.bold,
fontSize: _width / 15,
color: Colors.white),
),
new Padding(
padding: new EdgeInsets.only(
top: _height / 30, left: _width / 8, right: _width / 8),
child: new Text(
'Snowboarder, Superhero and writer.\nSometime I work at google as Executive Chairman ',
style: new TextStyle(
fontWeight: FontWeight.normal,
fontSize: _width / 25,
color: Colors.white),
textAlign: TextAlign.center,
),
),
new Divider(
height: _height / 30,
color: Colors.white,
),
new Row(
children: <Widget>[
rowCell(343, 'POSTS'),
rowCell(673826, 'FOLLOWERS'),
rowCell(275, 'FOLLOWING'),
],
),
new Divider(height: _height / 30, color: Colors.white),
new Padding(
padding: new EdgeInsets.only(
left: _width / 8, right: _width / 8),
child: new FlatButton(
onPressed: () {},
child: new Container(
child: new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Icon(Icons.person),
new SizedBox(
width: _width / 30,
),
new Text('FOLLOW')
],
)),
color: Colors.blue[50],
),
),
],
),
))
],
);
}
Widget rowCell(int count, String type) => new Expanded(
child: new Column(
children: <Widget>[
new Text(
'$count',
style: new TextStyle(color: Colors.white),
),
new Text(type,
style: new TextStyle(
color: Colors.white, fontWeight: FontWeight.normal))
],
));
}

Resources