Flutter — Firebase CRUD list view

abstract class Authentication {
Stream<String> getUid();
Future<FirebaseUser> loadUser();
void refresh();
}

class AuthenticationImpl extends Authentication {
final String _auth = FirebaseAuth.instance;
final StreamController<String> _uidStream = BehaviorSubject<String>();

AuthenticationImpl() {
refresh();
}

@override
Future<String> loadUser() async {
return _auth.currentUser().uid;
}

@override
Stream<String> getUid() => _uidStream.stream;

@override
void refresh() => loadUser()
.then((user) =>
_uidStream.add(user != null ? user.uid : null));
}
Ioc().bind("auth", (ioc) => AuthenticationImpl(), singleton: true);
/// Representing a single note in the database
class Note {
/// Actual note data
final String note;

/// Date the note was logged. Also used as a key.
final DateTime date;

Note(this.note, this.date);

factory Note.fromJson(Map<String, dynamic> json) => _$NoteFromJson(json);

Map<String, dynamic> toJson() => _$NoteToJson(this);
}

Storage

abstract class UserSettingsRepo {
Stream<List<Note>> get stream;
void storeNote(Note note);
void removeNote(Note note);
}

class UserSettingsRepoImpl implements UserSettingsRepo {
StreamController<List<Note>> _streamController =
BehaviorSubject<List<Note>>();

Authentication _auth = Ioc().use<Authentication>("auth");

// Cache of users store
List<Note> _map = List();

UserSettingsRepoImpl() {
_streamController.add(_map);

_auth.getUid().listen((uid) {
if (uid != null) {
Firestore.instance
.collection("users")
.document(uid)
.snapshots()
.listen((l) {
_map = l.data.map(/** Map to note list.../*);
_streamController.add(UserSettings(_map));

});
}
});
}
@override
Stream<UserSettings> get stream => _streamController.stream;

@override
void storeNote(Technique technique, Note note) async {
_map.removeWhere((currentNote) => note.date == currentNote.date);
techniques.add(note);
_updateFirestoreWithCache();
}@override
void removeNote(Technique technique, Note note) {
_map.removeWhere((currentNote) => note.date == currentNote.date);

_updateFirestoreWithCache();
}

void
_updateFirestoreWithCache() async {
var user = await _auth.loadUser();
Firestore.instance.collection("users").document(user.uid).setData(
_map.map((k, v) => MapEntry(k, v.map((n) => n.toJson()).toList())));
}
}
class NoteListBloc extends Bloc {
UserSettingsRepo _repo = Ioc().use<List<Note>>("UserSettingsRepo");

Stream<List<Note>> get stream => _repo.stream;

@override
void dispose() {
stream.drain();
}

void addNote(Note note) {
_repo.storeNote(note);
}

void removeNote(Note note) {
_repo.removeNote(technique, note);
}
}
class NotesList extends StatefulWidget {
final Technique technique;

const NotesList({Key key, @required this.technique}) : super(key: key);

@override
_NotesListState createState() => _NotesListState();
}

class _NotesListState extends State<NotesList> {
NoteListBloc _bloc;

@override
Widget build(BuildContext context) {
return StreamBuilder<UserSettings>(
stream: _bloc.stream,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Container(
child: Text(
"You don't have any notes"
));
}

return ListView(
shrinkWrap: true,
children: snapshot.data
.map((note) => NoteTile(
note: note,
onClick: () => addNoteModal(
context, _bloc.addNote,
note: note),
))
.toList(),
);
});
}

@override
void didChangeDependencies() {
_bloc = NoteListBloc();
super.didChangeDependencies();
}

@override
void dispose() {
_bloc.dispose();
super.dispose();
}
}
class NoteTile extends StatelessWidget {
final Note note;
final Function onClick;

const NoteTile({Key key, @required this.note, @required this.onClick})
: super(key: key);

@override
Widget build(BuildContext context) => Dismissible(
key: Key(note.date.toIso8601String()),
confirmDismiss: confirm,
child: ListTile(
onTap: onClick,
title: Text(
note.note
),
leading: Icon(Icons.note),
subtitle: Text(displayableDate(note.date)),
trailing:
Icon(Icons.navigate_next),
),
);

Future<bool> confirm(DismissDirection direction) {
return Future.value(false);
}
}
Future<bool> confirm(DismissDirection direction, BuildContext context) async {
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Confirm delete"),
content: const Text("Are you sure you wish to delete this note?"),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text("Delete")),
FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: const Text("Cancel"),
)
],
);
});
}
confirmDismiss: (direction) async => await confirm(direction, context),
class NoteTile extends StatelessWidget {
final Note note;
final Function onClick;
final Function onDelete;

const NoteTile(
{Key key, @required this.note, @required this.onClick, @required this.onDelete})
: super(key: key);
...
onDismissed: (_) => onDelete(),
...
}
NoteTile(
note: note,
onDelete: () => _bloc.removeNote(note),
onClick: () =>
addNoteModal(
context, _bloc.addNote,
note: note),
)

--

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Getting Codecov.io working in Azure Pipelines

Travis Authentication with Heroku

Integrating Azure API Management with Azure Virtual Network and Azure Application Gateway

The best free certifications for coders!

Javascript libraries used at Microsoft

Aleo: Breaking Down

The Two Worlds of ETHDenver 2020

Intro to SPARQL (Part 2)

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
Daniel Llewellyn

Daniel Llewellyn

More from Medium

FLUTTER FESTIVAL 2022

Easy way to implement User Session-Timeout feature in Flutter

Dart: Constructors

Developing Flutter Native Plugin — a real world scenario