Reputation: 11
Background:
I am trying to produce a scrollable listview of images. This screen will be a three stage process in that it will:
On each press, the image selected will build in to a new array, ultimately forming another kind of Image View made up of the three chosen images to be shown along the bottom, with a button to click that will then read the "sentence" of the 3 selected images.
What I have at the moment:
I have the initial ListView displaying with the correct filtered images, provided through a StreamProvider which calls a class containing the ListView. Currently this is filtered as I have hard coded the query.
This is the screen that ultimately displays the ListView
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:showmeapp/Screens/Home/image_list.dart';
import 'package:showmeapp/Services/database.dart';
import 'package:showmeapp/models/new_image.dart';
class Sentence extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamProvider<List<NewImage>>.value(
value: FirestoreDatabaseService().firestoreImages,
child: Scaffold(
backgroundColor: Colors.blue[300],
appBar: AppBar(
title: Text('Make A Sentence'),
backgroundColor: Colors.blue[700],
elevation: 0.0,
),
body: ImageList(),
)
);
}
}
This is the ImageList class that is called from that screen
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:showmeapp/Screens/Home/image_card.dart';
import 'package:showmeapp/models/new_image.dart';
class ImageList extends StatefulWidget {
@override
_ImageListState createState() => _ImageListState();
}
class _ImageListState extends State<ImageList> {
@override
Widget build(BuildContext context) {
final images = Provider.of<List<NewImage>>(context) ?? [];
final requestImages = images.where((element) => element.imageClass == 'request').toList();
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: requestImages.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 24.0),
height: MediaQuery.of(context).size.height * 0.35,
child: ImageCard(newImage: requestImages[index]),
);
},
);
}
}
and this is the custom ImageCard class that is called from the ImageList class
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:showmeapp/Screens/Home/image_list.dart';
import 'package:showmeapp/models/new_image.dart';
class ImageCard extends StatelessWidget {
final NewImage newImage;
ImageCard({this.newImage});
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
width: MediaQuery.of(context).size.width * 0.3,
height: MediaQuery.of(context).size.height * 0.2,
child: Card(
elevation: 30.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
side: BorderSide(width: 2.0, color: Colors.blueAccent),
),
child: GestureDetector(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.asset(
'assets/${newImage.imageLocation}',
scale: 0.8,
height: MediaQuery.of(context).size.height * 0.2,
width: MediaQuery.of(context).size.width * 0.1,
),
),
onTap: () {
// String optionSelected = newImage.imageClass;
},
),
),
),
Container(
width: MediaQuery.of(context).size.width * 0.3,
child: Padding(
padding: EdgeInsets.all(5.0),
child: Center(
child: Text(
newImage.imageDescription,
style: TextStyle(
fontSize: 40.0,
),
),
),
),
)
],
);
}
}
And finally, just for completeness, this is the Firebase related functions and calls behind all of that
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:showmeapp/models/new_image.dart';
// A class for the Firestore Database Service
class FirestoreDatabaseService {
final String iid;
// Constructor for Database Class
FirestoreDatabaseService({ this.iid });
// create a reference to the Firestore 'images' collection
final CollectionReference firestoreImageCollection = FirebaseFirestore.instance.collection('images');
// Add or update image data - This will be useful for initial creation of image meta data for the app
Future updateFirestoreImageData(String description, String location, String type, String category) async {
return await firestoreImageCollection.doc(iid).set({
'image_category': category,
'image_description': description,
'image_location': location,
'image_type': type
});
}
// new image list from snapshot
List<NewImage> _newImageFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((doc) {
return NewImage(
imageClass: doc.data()['image_class'] ?? '',
imageType: doc.data()['image_type'] ?? '',
imageCategory: doc.data()['image_category'] ?? '',
imageLocation: doc.data()['image_location'] ?? '',
imageDescription: doc.data()['image_description'] ?? ''
);
}).toList();
}
// Get images from collection via stream
Stream<List<NewImage>> get firestoreImages {
return firestoreImageCollection.snapshots()
.map(_newImageFromSnapshot);
}
}
What I am struggling with:
As the user selects one of the 'request' images, I then want to pass through a value so that the next set of images displayed is of the type 'object'.
But if I pass this through, it always sends the Provider Of request as Null and returns nothing.
The code that doesn't work:
ImageList class
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:showmeapp/Screens/Home/image_card.dart';
import 'package:showmeapp/models/new_image.dart';
class ImageList extends StatefulWidget {
@override
_ImageListState createState() => _ImageListState();
String searchClass;
ImageList({ this.searchClass });
}
class _ImageListState extends State<ImageList> {
@override
Widget build(BuildContext context) {
final String searchClass = ImageList().searchClass;
final images = Provider.of<List<NewImage>>(context) ?? [];
final requestImages = images.where((element) => element.imageClass == searchClass.toString()).toList();
print('search class is: $searchClass');
print('Class search class is: ${ImageList().searchClass}');
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: requestImages.length,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 24.0),
height: MediaQuery.of(context).size.height * 0.35,
child: ImageCard(newImage: requestImages[index]),
);
},
);
}
}
Sentence Screen/Widget
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:showmeapp/Screens/Home/image_list.dart';
import 'package:showmeapp/Services/database.dart';
import 'package:showmeapp/models/new_image.dart';
class Sentence extends StatelessWidget {
@override
Widget build(BuildContext context) {
return StreamProvider<List<NewImage>>.value(
value: FirestoreDatabaseService().firestoreImages,
child: Scaffold(
backgroundColor: Colors.blue[300],
appBar: AppBar(
title: Text('Make A Sentence'),
backgroundColor: Colors.blue[700],
elevation: 0.0,
),
body: ImageList(searchClass: 'request',),
)
);
}
}
So, passing through that string of 'request' and including that in the where clause results in Null. It only works when I hard specify 'request' in the where clause. The above example isn't the action from the selected image, but is the same principal in where I am trying to tell it to initially load the 'request' images via a passed value. Once I can get the initial value through, then I am hoping any further passing will slot in to place :-)
I have tried variations of the variables being in the class, the class and the state (as it is now) but can't seem to get it to carry the value.
How would I achieve this? Is it possible using the StreamProvider / ListView builder?
UPDATE My NewImage class is as follows:
class NewImage {
final String imageType; // Whether the image is a Category image or Object Image
final String imageDescription; // Text to display with the image
final String imageLocation; // The URL or Location of the image
final String imageCategory; // The category that the image belongs to
final String imageClass; // A tag to identify the stage for sentences, such as Request, Object, Ending
NewImage({ this.imageType, this.imageDescription, this.imageLocation, this.imageCategory, this.imageClass });
}
Thanks
Andrew
Upvotes: 1
Views: 1255
Reputation: 459
I am not entirely sure if I understand your question, however I think your problem is caused by this line:
final images = Provider.of<List<NewImage>>(context) ?? [];
I assume your new_image.dart model is something like this:
class NewImage extends ChangeNotifier {
List<Image> _imageList = [];
List<Image> get imageList => _imageList;
void fetchImages() {
_imageList = fetchFunction();
notifyListeners();
}
....
}
If this is the case your code should be something like this:
final images = Provider.of<NewImage>(context, listen: false).imageList;
Upvotes: 1