26
#100DaysOfCodeChallenge - Crop Management Information System - Day 10
On Day 9 we access the device camera and stored the photo in cloud storage.
In this post, we will discuss retrieving real time data from cloud firestore and displaying it to the user.
Retrieval and display of data from cloud firestore are divided into three (3) main parts. This division is done for readability and maintainability purposes.
class FarmerService {
final farmerRef =
FirebaseFirestore.instance.collection('farmers').withConverter(
fromFirestore: (snapshot, _) =>
FarmerServiceModel.fromJson(snapshot.data()!),
toFirestore: (farmerModel, _) => farmerModel.toJson(),
);
Stream<QuerySnapshot<FarmerServiceModel>> getAllFarmers() {
return farmerRef.snapshots();
}
}
The getAllFarmers
method returns a Stream<QuerySnapshot<FarmerServiceModel>>
to AllFarmerCommand().run()
class AllFarmerCommand extends BaseCommand {
AllFarmerCommand(BuildContext c) : super(c);
Stream<QuerySnapshot<FarmerServiceModel>> run() {
farmerModel.farmers = farmerService.getAllFarmers();
return farmerModel.farmers;
}
}
This method calls farmerService.getAllFarmers();
, assigns the returned stream to FarmerModel.farmers and to the method the _AllFarmerScreenController()
class AllFarmerScreen extends StatefulWidget {
static String routeName = 'AllFarmerScreen';
const AllFarmerScreen({
Key? key,
}) : super(key: key);
_AllFarmerScreenController createState() => _AllFarmerScreenController();
}
This widget is the All Farmers Screen. It is comprised of a controller and layout widget. This widget is responsible for the screen logic and layout respectively.
class _AllFarmerScreenController extends State<AllFarmerScreen> {
late Stream<QuerySnapshot<FarmerServiceModel>> stream;
Widget build(BuildContext context) => _AllFarmerScreenView(this);
void initState() {
super.initState();
stream = AllFarmerCommand(context).run();
// stream = Provider.of<FarmerModel>(context, listen: false).farmers;
}
}
Stream<QuerySnapshot<FarmerServiceModel>>
from farmerService.getAllFarmers();
is stored in a variable called stream when iniState()
is called by flutter.
class _AllFarmerScreenView
extends WidgetView<AllFarmerScreen, _AllFarmerScreenController> {
final state;
const _AllFarmerScreenView(this.state) : super(state);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('All Farmers'),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: Center(
child: Text(
'Farmer Register',
style: TextStyles.title.bold,
),
),
),
Expanded(
child: StreamBuilder<QuerySnapshot<FarmerServiceModel>>(
stream: state.stream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: SpinKitDoubleBounce(
color: Colours.accentColor(context),
));
} else if (snapshot.hasError) {
SnackBars.errorSnackBar(
content: 'Something went wrong', context: context);
print('something went wrong');
return Text('Something went wrong');
} else if (snapshot.connectionState ==
ConnectionState.waiting) {
return Text("Loading");
} else
return ListView(
children: snapshot.data!.docs
.map((DocumentSnapshot<FarmerServiceModel> document) {
FarmerServiceModel farmer = document.data()!;
return FarmerIdentificationCard(
farmer: farmer,
);
}).toList(),
);
},
),
)
],
),
);
}
}
This widget is responsible for the layout of the AllFarmerScreen
. We will discuss the stream builder part of the code.
Expanded(
child: StreamBuilder<QuerySnapshot<FarmerServiceModel>>(
stream: state.stream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: SpinKitDoubleBounce(
color: Colours.accentColor(context),
));
} else if (snapshot.hasError) {
SnackBars.errorSnackBar(
content: 'Something went wrong', context: context);
print('something went wrong');
return Text('Something went wrong');
} else if (snapshot.connectionState ==
ConnectionState.waiting) {
return Text("Loading");
} else
return ListView(
children: snapshot.data!.docs
.map((DocumentSnapshot<FarmerServiceModel> document) {
FarmerServiceModel farmer = document.data()!;
return FarmerIdentificationCard(
farmer: farmer,
);
}).toList(),
);
},
),
)
Stream builder will receive updates every time a document is added, deleted or edited in the farmers collection.
Before we extract any documents from the snapshot, we do three (3) main checks. First, we check for data in the snapshot, secondly, we check for errors and lastly we check for the connection state. Once we pass the checks, we return a listview.
Within the listview, each document from the snapshot is converted to a FarmerIdentificationCard()
widget. Screenshot of the Identification card is shown below.
In this post, we discussed how to retrieve data from a cloud firestore collection as a Stream<QuerySnapshot
. We then used the snapshot to create a Farmer Identification Card widget for each document in the snapshot.
Thank you for reading my post. Feel free to subscribe below to join me on the #100DaysOfCodeChallenge or connect with me on LinkedIn and Twitter. You can also buy me a book to show your support.
26