Skip to content

Commit

Permalink
Merge pull request #2 from codediodeio/2-database
Browse files Browse the repository at this point in the history
feat: database service
  • Loading branch information
codediodeio committed May 14, 2019
2 parents 67f6ba9 + 9a5514a commit 4744229
Show file tree
Hide file tree
Showing 6 changed files with 279 additions and 40 deletions.
38 changes: 11 additions & 27 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import 'package:flutter/material.dart';
import 'package:firebase_analytics/observer.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'services/services.dart';
import 'screens/screens.dart';
import 'package:firebase_analytics/firebase_analytics.dart';

import 'package:provider/provider.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// Firebase Analytics
return MultiProvider(
providers: [
StreamProvider<Report>.value(stream: Global.reportRef.documentStream),
StreamProvider<FirebaseUser>.value(stream: AuthService().user),
],
child: MaterialApp(
// Firebase Analytics
navigatorObservers: [
FirebaseAnalyticsObserver(analytics: FirebaseAnalytics()),
],
Expand Down Expand Up @@ -40,29 +46,7 @@ class MyApp extends StatelessWidget {
),
buttonTheme: ButtonThemeData(),
),
),
);
}
}























56 changes: 47 additions & 9 deletions lib/screens/profile.dart
Original file line number Diff line number Diff line change
@@ -1,26 +1,64 @@
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import '../services/services.dart';
import '../shared/shared.dart';
import 'package:provider/provider.dart';

class ProfileScreen extends StatelessWidget {
final AuthService auth = AuthService();

@override
Widget build(BuildContext context) {
Report report = Provider.of<Report>(context);
FirebaseUser user = Provider.of<FirebaseUser>(context);

if (user != null) {

return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepOrange,
title: Text('profile'),
title: Text(user.displayName ?? 'Guest'),
),
body: Center(
child: FlatButton(
child: Text('logout'),
color: Colors.red,
onPressed: () async {
await auth.signOut();
Navigator.of(context)
.pushNamedAndRemoveUntil('/', (route) => false);
}),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (user.photoUrl != null)
Container(
width: 100,
height: 100,
margin: EdgeInsets.only(top: 50),
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
image: NetworkImage(user.photoUrl),
),
),
),
Text(user.email ?? '', style: Theme.of(context).textTheme.headline),
Spacer(),
if (report != null)
Text('${report.total ?? 0}',
style: Theme.of(context).textTheme.display3),
Text('Quizzes Completed',
style: Theme.of(context).textTheme.subhead),
Spacer(),
FlatButton(
child: Text('logout'),
color: Colors.red,
onPressed: () async {
await auth.signOut();
Navigator.of(context).pushNamedAndRemoveUntil('/', (route) => false);
}),
Spacer()
],
),
),
);
} else {
return LoadingScreen();
}
}

}
92 changes: 92 additions & 0 deletions lib/services/db.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'dart:async';
import 'package:rxdart/rxdart.dart';
import './globals.dart';



class Document<T> {
final Firestore _db = Firestore.instance;
final String path;
DocumentReference ref;

Document({ this.path }) {
ref = _db.document(path);
}

Future<T> getData() {
return ref.get().then((v) => Global.models[T](v.data) as T);
}

Stream<T> streamData() {
return ref.snapshots().map((v) => Global.models[T](v.data) as T);
}

Future<void> upsert(Map data) {
return ref.setData(Map<String, dynamic>.from(data), merge: true);
}

}

class Collection<T> {
final Firestore _db = Firestore.instance;
final String path;
CollectionReference ref;

Collection({ this.path }) {
ref = _db.collection(path);
}

Future<List<T>> getData() async {
var snapshots = await ref.getDocuments();
return snapshots.documents.map((doc) => Global.models[T](doc.data) as T ).toList();
}

Stream<List<T>> streamData() {
return ref.snapshots().map((list) => list.documents.map((doc) => Global.models[T](doc.data) as T) );
}


}


class UserData<T> {
final Firestore _db = Firestore.instance;
final FirebaseAuth _auth = FirebaseAuth.instance;
final String collection;

UserData({ this.collection });


Stream<T> get documentStream {

return Observable(_auth.onAuthStateChanged).switchMap((user) {
if (user != null) {
Document<T> doc = Document<T>(path: '$collection/${user.uid}');
return doc.streamData();
} else {
return Observable<T>.just(null);
}
}); //.shareReplay(maxSize: 1).doOnData((d) => print('777 $d'));// as Stream<T>;
}

Future<T> getDocument() async {
FirebaseUser user = await _auth.currentUser();

if (user != null) {
Document doc = Document<T>(path: '$collection/${user.uid}');
return doc.getData();
} else {
return null;
}

}

Future<void> upsert(Map data) async {
FirebaseUser user = await _auth.currentUser();
Document<T> ref = Document(path: '$collection/${user.uid}');
return ref.upsert(data);
}

}
12 changes: 12 additions & 0 deletions lib/services/globals.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,17 @@ class Global {

// Services
static final FirebaseAnalytics analytics = FirebaseAnalytics();

// Data Models
static final Map models = {
Topic: (data) => Topic.fromMap(data),
Quiz: (data) => Quiz.fromMap(data),
Report: (data) => Report.fromMap(data),
};

// Firestore References for Writes
static final Collection<Topic> topicsRef = Collection<Topic>(path: 'topics');
static final UserData<Report> reportRef = UserData<Report>(collection: 'reports');


}
96 changes: 92 additions & 4 deletions lib/services/models.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,92 @@
export 'auth.dart';
export 'models.dart';
export 'db.dart';
export 'globals.dart';
//// Embedded Maps
class Option {
String value;
String detail;
bool correct;

Option({ this.correct, this.value, this.detail });
Option.fromMap(Map data) {
value = data['value'];
detail = data['detail'] ?? '';
correct = data['correct'];
}
}


class Question {
String text;
List<Option> options;
Question({ this.options, this.text });

Question.fromMap(Map data) {
text = data['text'] ?? '';
options = (data['options'] as List ?? []).map((v) => Option.fromMap(v)).toList();
}
}

///// Database Collections
class Quiz {
String id;
String title;
String description;
String video;
String topic;
List<Question> questions;

Quiz({ this.title, this.questions, this.video, this.description, this.id, this.topic });

factory Quiz.fromMap(Map data) {
return Quiz(
id: data['id'] ?? '',
title: data['title'] ?? '',
topic: data['topic'] ?? '',
description: data['description'] ?? '',
video: data['video'] ?? '',
questions: (data['questions'] as List ?? []).map((v) => Question.fromMap(v)).toList()
);
}

}


class Topic {
final String id;
final String title;
final String description;
final String img;
final List<Quiz> quizzes;

Topic({ this.id, this.title, this.description, this.img, this.quizzes });

factory Topic.fromMap(Map data) {
return Topic(
id: data['id'] ?? '',
title: data['title'] ?? '',
description: data['description'] ?? '',
img: data['img'] ?? 'default.png',
quizzes: (data['quizzes'] as List ?? []).map((v) => Quiz.fromMap(v)).toList(), //data['quizzes'],
);
}

}


class Report {
String uid;
int total;
dynamic topics;

Report({ this.uid, this.topics, this.total });

factory Report.fromMap(Map data) {
return Report(
uid: data['uid'],
total: data['total'] ?? 0,
topics: data['topics'] ?? {},
);
}

}

25 changes: 25 additions & 0 deletions lib/shared/loader.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:flutter/material.dart';


class Loader extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
width: 250,
height: 250,
child: CircularProgressIndicator(),
);
}
}

class LoadingScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: Loader(),
),
);
}
}

0 comments on commit 4744229

Please sign in to comment.