Firebase, Provider and Flutter

Dependencies

Setup

Code

Movie Model

class Movie{
String title;
bool watched;
String id;
}
Movie.fromMap(Map<String, dynamic> data, this.id){
this.title = data["title"];
this.watched = data["watched"];
}

AppData Provider

import 'package:flutter/widgets.dart';

class AppProvider with ChangeNotifier{
List<Movie> movies = [];
}
runApp(
ChangeNotifierProvider<AppProvider>(
create: (_) => AppProvider(),
child: MyApp(),
),
);
FirebaseAuth auth;
User user; //null if not signed in
AppProvider() {
auth = FirebaseAuth.instance;
setupAuthListener();
}

Future<UserCredential> signIn(){
return auth.signInAnonymously();
}

setupAuthListener() {
auth.authStateChanges().listen((user) {
print("is user signed in: ${user != null} as ${user?.uid}");
this.user = user;
if(user != null){
load();
}
});
}
@override
void initState() {
super
.initState();
Provider.of<AppProvider>(context, listen: false).signIn();
}
load() async {
if (user == null || endReached || loading) return;
loading = true;

if (lastLoaded == null) {
movies.clear();
}

Query q = FirebaseFirestore.instance
.collection("Movies")
.where("uid", isEqualTo: user.uid)
.orderBy("title");

if (lastLoaded != null) {
print("Loading more: ${movies.length}");
q = q.startAfterDocument(lastLoaded);
}

QuerySnapshot query = await q.limit(5).get();

if(query.docs.length != 5){
endReached = true;
}

lastLoaded = query.docs.last;
query.docs.forEach((element) {
movies.add(Movie.fromMap(element.data(), element.id));
});
loading = false;
notifyListeners();
}

UI

class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(

);
}
}
body: Consumer<AppProvider>(
builder: (context, app, child) {

},
),
return Column(
children: [
Expanded(
child: ListView.builder(
itemBuilder: (context, index) {
return;
},
itemCount: app.movies.length,
),
),
TextField(),
],
);
if(index > app.movies.length - 2){
app.load();
}
class MovieView extends StatelessWidget {
Movie movie;
Function(bool) onChange;

MovieView(this.movie, this.onChange);

@override
Widget build(BuildContext context) {
return Container(
child: Row(
children: [
Checkbox(value: movie.watched, onChanged: onChange),
Expanded(child: Text(movie.title)),
],
),
);
}
}
return MovieView(app.movies[index], (v){
app.setWatched(app.movies[index].id, v);
});
void setWatched(String id, bool v) {
FirebaseFirestore.instance
.collection("Movies")
.doc(id)
.update({"watched": v});
movies.firstWhere((element) => element.id == id).watched = v;
notifyListeners();
}

void addMovie(String title) {
FirebaseFirestore.instance
.collection("Movies")
.add({"title": title, "watched": false, "uid": user.uid}).then((value){

movies.add(Movie(title, false, value.id));
notifyListeners();
});
}
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: _controller,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
borderSide: BorderSide(
color: Colors.blue,
width: 2.0,
style: BorderStyle.solid))),
onSubmitted: (s) {
app.addMovie(s);
_controller.clear();
},
),
),

Done!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Karthik Nooli

Karthik Nooli

Flutter Developer, co-lead for GDG Southampton