mirror of
https://github.com/flutter/samples.git
synced 2025-11-08 22:09:06 +00:00
Migrate web dashboard to null safety (#928)
* update dependencies * Update to cloud_firestore 2.x.x * run dart migrate * Fix analyzer warnings from null safety migration * Fix errors resulting from null safety migration * Fix info level warnings * run flutter format * fix tests * remove unused import, format
This commit is contained in:
@@ -15,9 +15,9 @@ abstract class DashboardApi {
|
|||||||
|
|
||||||
/// Manipulates [Category] data.
|
/// Manipulates [Category] data.
|
||||||
abstract class CategoryApi {
|
abstract class CategoryApi {
|
||||||
Future<Category> delete(String id);
|
Future<Category?> delete(String id);
|
||||||
|
|
||||||
Future<Category> get(String id);
|
Future<Category?> get(String id);
|
||||||
|
|
||||||
Future<Category> insert(Category category);
|
Future<Category> insert(Category category);
|
||||||
|
|
||||||
@@ -30,9 +30,9 @@ abstract class CategoryApi {
|
|||||||
|
|
||||||
/// Manipulates [Entry] data.
|
/// Manipulates [Entry] data.
|
||||||
abstract class EntryApi {
|
abstract class EntryApi {
|
||||||
Future<Entry> delete(String categoryId, String id);
|
Future<Entry?> delete(String categoryId, String id);
|
||||||
|
|
||||||
Future<Entry> get(String categoryId, String id);
|
Future<Entry?> get(String categoryId, String id);
|
||||||
|
|
||||||
Future<Entry> insert(String categoryId, Entry entry);
|
Future<Entry> insert(String categoryId, Entry entry);
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ class Category {
|
|||||||
String name;
|
String name;
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
String id;
|
String? id;
|
||||||
|
|
||||||
Category(this.name);
|
Category(this.name);
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ class Entry {
|
|||||||
DateTime time;
|
DateTime time;
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
String id;
|
String? id;
|
||||||
|
|
||||||
Entry(this.value, this.time);
|
Entry(this.value, this.time);
|
||||||
|
|
||||||
|
|||||||
@@ -13,15 +13,15 @@ class FirebaseDashboardApi implements DashboardApi {
|
|||||||
@override
|
@override
|
||||||
final CategoryApi categories;
|
final CategoryApi categories;
|
||||||
|
|
||||||
FirebaseDashboardApi(Firestore firestore, String userId)
|
FirebaseDashboardApi(FirebaseFirestore firestore, String userId)
|
||||||
: entries = FirebaseEntryApi(firestore, userId),
|
: entries = FirebaseEntryApi(firestore, userId),
|
||||||
categories = FirebaseCategoryApi(firestore, userId);
|
categories = FirebaseCategoryApi(firestore, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
class FirebaseEntryApi implements EntryApi {
|
class FirebaseEntryApi implements EntryApi {
|
||||||
final Firestore firestore;
|
final FirebaseFirestore firestore;
|
||||||
final String userId;
|
final String userId;
|
||||||
final CollectionReference _categoriesRef;
|
final CollectionReference<Map<String, dynamic>> _categoriesRef;
|
||||||
|
|
||||||
FirebaseEntryApi(this.firestore, this.userId)
|
FirebaseEntryApi(this.firestore, this.userId)
|
||||||
: _categoriesRef = firestore.collection('users/$userId/categories');
|
: _categoriesRef = firestore.collection('users/$userId/categories');
|
||||||
@@ -29,10 +29,10 @@ class FirebaseEntryApi implements EntryApi {
|
|||||||
@override
|
@override
|
||||||
Stream<List<Entry>> subscribe(String categoryId) {
|
Stream<List<Entry>> subscribe(String categoryId) {
|
||||||
var snapshots =
|
var snapshots =
|
||||||
_categoriesRef.document(categoryId).collection('entries').snapshots();
|
_categoriesRef.doc(categoryId).collection('entries').snapshots();
|
||||||
var result = snapshots.map((querySnapshot) {
|
var result = snapshots.map<List<Entry>>((querySnapshot) {
|
||||||
return querySnapshot.documents.map((snapshot) {
|
return querySnapshot.docs.map<Entry>((snapshot) {
|
||||||
return Entry.fromJson(snapshot.data)..id = snapshot.documentID;
|
return Entry.fromJson(snapshot.data())..id = snapshot.id;
|
||||||
}).toList();
|
}).toList();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -41,8 +41,8 @@ class FirebaseEntryApi implements EntryApi {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Entry> delete(String categoryId, String id) async {
|
Future<Entry> delete(String categoryId, String id) async {
|
||||||
var document = _categoriesRef.document('$categoryId/entries/$id');
|
var document = _categoriesRef.doc('$categoryId/entries/$id');
|
||||||
var entry = await get(categoryId, document.documentID);
|
var entry = await get(categoryId, document.id);
|
||||||
|
|
||||||
await document.delete();
|
await document.delete();
|
||||||
|
|
||||||
@@ -52,18 +52,18 @@ class FirebaseEntryApi implements EntryApi {
|
|||||||
@override
|
@override
|
||||||
Future<Entry> insert(String categoryId, Entry entry) async {
|
Future<Entry> insert(String categoryId, Entry entry) async {
|
||||||
var document = await _categoriesRef
|
var document = await _categoriesRef
|
||||||
.document(categoryId)
|
.doc(categoryId)
|
||||||
.collection('entries')
|
.collection('entries')
|
||||||
.add(entry.toJson());
|
.add(entry.toJson());
|
||||||
return await get(categoryId, document.documentID);
|
return await get(categoryId, document.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Entry>> list(String categoryId) async {
|
Future<List<Entry>> list(String categoryId) async {
|
||||||
var entriesRef = _categoriesRef.document(categoryId).collection('entries');
|
var entriesRef = _categoriesRef.doc(categoryId).collection('entries');
|
||||||
var querySnapshot = await entriesRef.getDocuments();
|
var querySnapshot = await entriesRef.get();
|
||||||
var entries = querySnapshot.documents
|
var entries = querySnapshot.docs
|
||||||
.map((doc) => Entry.fromJson(doc.data)..id = doc.documentID)
|
.map((doc) => Entry.fromJson(doc.data())..id = doc.id)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
return entries;
|
return entries;
|
||||||
@@ -71,24 +71,24 @@ class FirebaseEntryApi implements EntryApi {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Entry> update(String categoryId, String id, Entry entry) async {
|
Future<Entry> update(String categoryId, String id, Entry entry) async {
|
||||||
var document = _categoriesRef.document('$categoryId/entries/$id');
|
var document = _categoriesRef.doc('$categoryId/entries/$id');
|
||||||
await document.setData(entry.toJson());
|
await document.update(entry.toJson());
|
||||||
var snapshot = await document.get();
|
var snapshot = await document.get();
|
||||||
return Entry.fromJson(snapshot.data)..id = snapshot.documentID;
|
return Entry.fromJson(snapshot.data()!)..id = snapshot.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Entry> get(String categoryId, String id) async {
|
Future<Entry> get(String categoryId, String id) async {
|
||||||
var document = _categoriesRef.document('$categoryId/entries/$id');
|
var document = _categoriesRef.doc('$categoryId/entries/$id');
|
||||||
var snapshot = await document.get();
|
var snapshot = await document.get();
|
||||||
return Entry.fromJson(snapshot.data)..id = snapshot.documentID;
|
return Entry.fromJson(snapshot.data()!)..id = snapshot.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FirebaseCategoryApi implements CategoryApi {
|
class FirebaseCategoryApi implements CategoryApi {
|
||||||
final Firestore firestore;
|
final FirebaseFirestore firestore;
|
||||||
final String userId;
|
final String userId;
|
||||||
final CollectionReference _categoriesRef;
|
final CollectionReference<Map<String, dynamic>> _categoriesRef;
|
||||||
|
|
||||||
FirebaseCategoryApi(this.firestore, this.userId)
|
FirebaseCategoryApi(this.firestore, this.userId)
|
||||||
: _categoriesRef = firestore.collection('users/$userId/categories');
|
: _categoriesRef = firestore.collection('users/$userId/categories');
|
||||||
@@ -96,9 +96,9 @@ class FirebaseCategoryApi implements CategoryApi {
|
|||||||
@override
|
@override
|
||||||
Stream<List<Category>> subscribe() {
|
Stream<List<Category>> subscribe() {
|
||||||
var snapshots = _categoriesRef.snapshots();
|
var snapshots = _categoriesRef.snapshots();
|
||||||
var result = snapshots.map((querySnapshot) {
|
var result = snapshots.map<List<Category>>((querySnapshot) {
|
||||||
return querySnapshot.documents.map((snapshot) {
|
return querySnapshot.docs.map<Category>((snapshot) {
|
||||||
return Category.fromJson(snapshot.data)..id = snapshot.documentID;
|
return Category.fromJson(snapshot.data())..id = snapshot.id;
|
||||||
}).toList();
|
}).toList();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -107,8 +107,8 @@ class FirebaseCategoryApi implements CategoryApi {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Category> delete(String id) async {
|
Future<Category> delete(String id) async {
|
||||||
var document = _categoriesRef.document(id);
|
var document = _categoriesRef.doc(id);
|
||||||
var categories = await get(document.documentID);
|
var categories = await get(document.id);
|
||||||
|
|
||||||
await document.delete();
|
await document.delete();
|
||||||
|
|
||||||
@@ -117,22 +117,22 @@ class FirebaseCategoryApi implements CategoryApi {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Category> get(String id) async {
|
Future<Category> get(String id) async {
|
||||||
var document = _categoriesRef.document(id);
|
var document = _categoriesRef.doc(id);
|
||||||
var snapshot = await document.get();
|
var snapshot = await document.get();
|
||||||
return Category.fromJson(snapshot.data)..id = snapshot.documentID;
|
return Category.fromJson(snapshot.data()!)..id = snapshot.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Category> insert(Category category) async {
|
Future<Category> insert(Category category) async {
|
||||||
var document = await _categoriesRef.add(category.toJson());
|
var document = await _categoriesRef.add(category.toJson());
|
||||||
return await get(document.documentID);
|
return await get(document.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Category>> list() async {
|
Future<List<Category>> list() async {
|
||||||
var querySnapshot = await _categoriesRef.getDocuments();
|
var querySnapshot = await _categoriesRef.get();
|
||||||
var categories = querySnapshot.documents
|
var categories = querySnapshot.docs
|
||||||
.map((doc) => Category.fromJson(doc.data)..id = doc.documentID)
|
.map((doc) => Category.fromJson(doc.data())..id = doc.id)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
return categories;
|
return categories;
|
||||||
@@ -140,9 +140,9 @@ class FirebaseCategoryApi implements CategoryApi {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Category> update(Category category, String id) async {
|
Future<Category> update(Category category, String id) async {
|
||||||
var document = _categoriesRef.document(id);
|
var document = _categoriesRef.doc(id);
|
||||||
await document.setData(category.toJson());
|
await document.update(category.toJson());
|
||||||
var snapshot = await document.get();
|
var snapshot = await document.get();
|
||||||
return Category.fromJson(snapshot.data)..id = snapshot.documentID;
|
return Category.fromJson(snapshot.data()!)..id = snapshot.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:uuid/uuid.dart' as uuid;
|
import 'package:uuid/uuid.dart' as uuid;
|
||||||
|
|
||||||
import 'api.dart';
|
import 'api.dart';
|
||||||
@@ -30,7 +31,7 @@ class MockDashboardApi implements DashboardApi {
|
|||||||
for (var i = 0; i < 30; i++) {
|
for (var i = 0; i < 30; i++) {
|
||||||
var date = monthAgo.add(Duration(days: i));
|
var date = monthAgo.add(Duration(days: i));
|
||||||
var value = Random().nextInt(6) + 1;
|
var value = Random().nextInt(6) + 1;
|
||||||
await entries.insert(category.id, Entry(value, date));
|
await entries.insert(category.id!, Entry(value, date));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,20 +43,20 @@ class MockCategoryApi implements CategoryApi {
|
|||||||
StreamController<List<Category>>.broadcast();
|
StreamController<List<Category>>.broadcast();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Category> delete(String id) async {
|
Future<Category?> delete(String id) async {
|
||||||
var removed = _storage.remove(id);
|
var removed = _storage.remove(id);
|
||||||
_emit();
|
_emit();
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Category> get(String id) async {
|
Future<Category?> get(String id) async {
|
||||||
return _storage[id];
|
return _storage[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Category> insert(Category category) async {
|
Future<Category> insert(Category category) async {
|
||||||
var id = uuid.Uuid().v4();
|
var id = const uuid.Uuid().v4();
|
||||||
var newCategory = Category(category.name)..id = id;
|
var newCategory = Category(category.name)..id = id;
|
||||||
_storage[id] = newCategory;
|
_storage[id] = newCategory;
|
||||||
_emit();
|
_emit();
|
||||||
@@ -88,14 +89,14 @@ class MockEntryApi implements EntryApi {
|
|||||||
StreamController.broadcast();
|
StreamController.broadcast();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Entry> delete(String categoryId, String id) async {
|
Future<Entry?> delete(String categoryId, String id) async {
|
||||||
_emit(categoryId);
|
_emit(categoryId);
|
||||||
return _storage.remove('$categoryId-$id');
|
return _storage.remove('$categoryId-$id');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Entry> insert(String categoryId, Entry entry) async {
|
Future<Entry> insert(String categoryId, Entry entry) async {
|
||||||
var id = uuid.Uuid().v4();
|
var id = const uuid.Uuid().v4();
|
||||||
var newEntry = Entry(entry.value, entry.time)..id = id;
|
var newEntry = Entry(entry.value, entry.time)..id = id;
|
||||||
_storage['$categoryId-$id'] = newEntry;
|
_storage['$categoryId-$id'] = newEntry;
|
||||||
_emit(categoryId);
|
_emit(categoryId);
|
||||||
@@ -104,10 +105,12 @@ class MockEntryApi implements EntryApi {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<List<Entry>> list(String categoryId) async {
|
Future<List<Entry>> list(String categoryId) async {
|
||||||
return _storage.keys
|
var list = _storage.keys
|
||||||
.where((k) => k.startsWith(categoryId))
|
.where((k) => k.startsWith(categoryId))
|
||||||
.map((k) => _storage[k])
|
.map((k) => _storage[k])
|
||||||
|
.whereNotNull()
|
||||||
.toList();
|
.toList();
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -127,14 +130,14 @@ class MockEntryApi implements EntryApi {
|
|||||||
void _emit(String categoryId) {
|
void _emit(String categoryId) {
|
||||||
var entries = _storage.keys
|
var entries = _storage.keys
|
||||||
.where((k) => k.startsWith(categoryId))
|
.where((k) => k.startsWith(categoryId))
|
||||||
.map((k) => _storage[k])
|
.map((k) => _storage[k]!)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
_streamController.add(_EntriesEvent(categoryId, entries));
|
_streamController.add(_EntriesEvent(categoryId, entries));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Entry> get(String categoryId, String id) async {
|
Future<Entry?> get(String categoryId, String id) async {
|
||||||
return _storage['$categoryId-$id'];
|
return _storage['$categoryId-$id'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import 'pages/sign_in.dart';
|
|||||||
/// The global state the app.
|
/// The global state the app.
|
||||||
class AppState {
|
class AppState {
|
||||||
final Auth auth;
|
final Auth auth;
|
||||||
DashboardApi api;
|
DashboardApi? api;
|
||||||
|
|
||||||
AppState(this.auth);
|
AppState(this.auth);
|
||||||
}
|
}
|
||||||
@@ -33,19 +33,19 @@ class DashboardApp extends StatefulWidget {
|
|||||||
static DashboardApi _mockApiBuilder(User user) =>
|
static DashboardApi _mockApiBuilder(User user) =>
|
||||||
MockDashboardApi()..fillWithMockData();
|
MockDashboardApi()..fillWithMockData();
|
||||||
static DashboardApi _apiBuilder(User user) =>
|
static DashboardApi _apiBuilder(User user) =>
|
||||||
FirebaseDashboardApi(Firestore.instance, user.uid);
|
FirebaseDashboardApi(FirebaseFirestore.instance, user.uid);
|
||||||
|
|
||||||
final Auth auth;
|
final Auth auth;
|
||||||
final ApiBuilder apiBuilder;
|
final ApiBuilder apiBuilder;
|
||||||
|
|
||||||
/// Runs the app using Firebase
|
/// Runs the app using Firebase
|
||||||
DashboardApp.firebase({Key key})
|
DashboardApp.firebase({Key? key})
|
||||||
: auth = FirebaseAuthService(),
|
: auth = FirebaseAuthService(),
|
||||||
apiBuilder = _apiBuilder,
|
apiBuilder = _apiBuilder,
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
/// Runs the app using mock data
|
/// Runs the app using mock data
|
||||||
DashboardApp.mock({Key key})
|
DashboardApp.mock({Key? key})
|
||||||
: auth = MockAuthService(),
|
: auth = MockAuthService(),
|
||||||
apiBuilder = _mockApiBuilder,
|
apiBuilder = _mockApiBuilder,
|
||||||
super(key: key);
|
super(key: key);
|
||||||
@@ -55,7 +55,7 @@ class DashboardApp extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _DashboardAppState extends State<DashboardApp> {
|
class _DashboardAppState extends State<DashboardApp> {
|
||||||
AppState _appState;
|
late final AppState _appState;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -80,13 +80,13 @@ class _DashboardAppState extends State<DashboardApp> {
|
|||||||
/// Switches between showing the [SignInPage] or [HomePage], depending on
|
/// Switches between showing the [SignInPage] or [HomePage], depending on
|
||||||
/// whether or not the user is signed in.
|
/// whether or not the user is signed in.
|
||||||
class SignInSwitcher extends StatefulWidget {
|
class SignInSwitcher extends StatefulWidget {
|
||||||
final AppState appState;
|
final AppState? appState;
|
||||||
final ApiBuilder apiBuilder;
|
final ApiBuilder? apiBuilder;
|
||||||
|
|
||||||
const SignInSwitcher({
|
const SignInSwitcher({
|
||||||
this.appState,
|
this.appState,
|
||||||
this.apiBuilder,
|
this.apiBuilder,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -107,14 +107,14 @@ class _SignInSwitcherState extends State<SignInSwitcher> {
|
|||||||
onSignOut: _handleSignOut,
|
onSignOut: _handleSignOut,
|
||||||
)
|
)
|
||||||
: SignInPage(
|
: SignInPage(
|
||||||
auth: widget.appState.auth,
|
auth: widget.appState!.auth,
|
||||||
onSuccess: _handleSignIn,
|
onSuccess: _handleSignIn,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleSignIn(User user) {
|
void _handleSignIn(User user) {
|
||||||
widget.appState.api = widget.apiBuilder(user);
|
widget.appState!.api = widget.apiBuilder!(user);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSignedIn = true;
|
_isSignedIn = true;
|
||||||
@@ -122,7 +122,7 @@ class _SignInSwitcherState extends State<SignInSwitcher> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future _handleSignOut() async {
|
Future _handleSignOut() async {
|
||||||
await widget.appState.auth.signOut();
|
await widget.appState!.auth.signOut();
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSignedIn = false;
|
_isSignedIn = false;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// for details. All rights reserved. Use of this source code is governed by a
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:firebase_auth/firebase_auth.dart' hide FirebaseUser;
|
import 'package:firebase_auth/firebase_auth.dart' hide User;
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:google_sign_in/google_sign_in.dart';
|
import 'package:google_sign_in/google_sign_in.dart';
|
||||||
|
|
||||||
@@ -25,21 +25,21 @@ class FirebaseAuthService implements Auth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<User> _signIn() async {
|
Future<User> _signIn() async {
|
||||||
GoogleSignInAccount googleUser;
|
GoogleSignInAccount? googleUser;
|
||||||
if (await isSignedIn) {
|
if (await isSignedIn) {
|
||||||
googleUser = await _googleSignIn.signInSilently();
|
googleUser = await _googleSignIn.signInSilently();
|
||||||
} else {
|
} else {
|
||||||
googleUser = await _googleSignIn.signIn();
|
googleUser = await _googleSignIn.signIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
var googleAuth = await googleUser.authentication;
|
var googleAuth = await googleUser!.authentication;
|
||||||
|
|
||||||
var credential = GoogleAuthProvider.getCredential(
|
var credential = GoogleAuthProvider.credential(
|
||||||
accessToken: googleAuth.accessToken, idToken: googleAuth.idToken);
|
accessToken: googleAuth.accessToken, idToken: googleAuth.idToken);
|
||||||
|
|
||||||
var authResult = await _auth.signInWithCredential(credential);
|
var authResult = await _auth.signInWithCredential(credential);
|
||||||
|
|
||||||
return _FirebaseUser(authResult.user.uid);
|
return _FirebaseUser(authResult.user!.uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
// for details. All rights reserved. Use of this source code is governed by a
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'auth.dart';
|
import 'auth.dart';
|
||||||
|
|
||||||
class MockAuthService implements Auth {
|
class MockAuthService implements Auth {
|
||||||
@@ -12,11 +10,6 @@ class MockAuthService implements Auth {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<User> signIn() async {
|
Future<User> signIn() async {
|
||||||
// Sign in will randomly fail 25% of the time.
|
|
||||||
var random = Random();
|
|
||||||
if (random.nextInt(4) == 0) {
|
|
||||||
throw SignInException();
|
|
||||||
}
|
|
||||||
return MockUser();
|
return MockUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ import '../app.dart';
|
|||||||
import '../widgets/category_chart.dart';
|
import '../widgets/category_chart.dart';
|
||||||
|
|
||||||
class DashboardPage extends StatelessWidget {
|
class DashboardPage extends StatelessWidget {
|
||||||
const DashboardPage({Key key}) : super(key: key);
|
const DashboardPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var appState = Provider.of<AppState>(context);
|
var appState = Provider.of<AppState>(context);
|
||||||
return FutureBuilder<List<Category>>(
|
return FutureBuilder<List<Category>>(
|
||||||
future: appState.api.categories.list(),
|
future: appState.api!.categories.list(),
|
||||||
builder: (context, futureSnapshot) {
|
builder: (context, futureSnapshot) {
|
||||||
if (!futureSnapshot.hasData) {
|
if (!futureSnapshot.hasData) {
|
||||||
return const Center(
|
return const Center(
|
||||||
@@ -25,7 +25,7 @@ class DashboardPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
return StreamBuilder<List<Category>>(
|
return StreamBuilder<List<Category>>(
|
||||||
initialData: futureSnapshot.data,
|
initialData: futureSnapshot.data,
|
||||||
stream: appState.api.categories.subscribe(),
|
stream: appState.api!.categories.subscribe(),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.data == null) {
|
if (snapshot.data == null) {
|
||||||
return const Center(
|
return const Center(
|
||||||
@@ -41,9 +41,9 @@ class DashboardPage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Dashboard extends StatelessWidget {
|
class Dashboard extends StatelessWidget {
|
||||||
final List<Category> categories;
|
final List<Category>? categories;
|
||||||
|
|
||||||
const Dashboard(this.categories, {Key key}) : super(key: key);
|
const Dashboard(this.categories, {Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -55,7 +55,7 @@ class Dashboard extends StatelessWidget {
|
|||||||
maxCrossAxisExtent: 500,
|
maxCrossAxisExtent: 500,
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
...categories.map(
|
...categories!.map(
|
||||||
(category) => Card(
|
(category) => Card(
|
||||||
child: CategoryChart(
|
child: CategoryChart(
|
||||||
category: category,
|
category: category,
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
// for details. All rights reserved. Use of this source code is governed by a
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart' as intl;
|
import 'package:intl/intl.dart' as intl;
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
@@ -12,14 +14,14 @@ import '../widgets/categories_dropdown.dart';
|
|||||||
import '../widgets/dialogs.dart';
|
import '../widgets/dialogs.dart';
|
||||||
|
|
||||||
class EntriesPage extends StatefulWidget {
|
class EntriesPage extends StatefulWidget {
|
||||||
const EntriesPage({Key key}) : super(key: key);
|
const EntriesPage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_EntriesPageState createState() => _EntriesPageState();
|
_EntriesPageState createState() => _EntriesPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _EntriesPageState extends State<EntriesPage> {
|
class _EntriesPageState extends State<EntriesPage> {
|
||||||
Category _selected;
|
Category? _selected;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -27,14 +29,14 @@ class _EntriesPageState extends State<EntriesPage> {
|
|||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
CategoryDropdown(
|
CategoryDropdown(
|
||||||
api: appState.api.categories,
|
api: appState.api!.categories,
|
||||||
onSelected: (category) => setState(() => _selected = category)),
|
onSelected: (category) => setState(() => _selected = category)),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _selected == null
|
child: _selected == null
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
: EntriesList(
|
: EntriesList(
|
||||||
category: _selected,
|
category: _selected,
|
||||||
api: appState.api.entries,
|
api: appState.api!.entries,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -43,13 +45,13 @@ class _EntriesPageState extends State<EntriesPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class EntriesList extends StatefulWidget {
|
class EntriesList extends StatefulWidget {
|
||||||
final Category category;
|
final Category? category;
|
||||||
final EntryApi api;
|
final EntryApi api;
|
||||||
|
|
||||||
EntriesList({
|
EntriesList({
|
||||||
@required this.category,
|
this.category,
|
||||||
@required this.api,
|
required this.api,
|
||||||
}) : super(key: ValueKey(category.id));
|
}) : super(key: ValueKey(category?.id));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_EntriesListState createState() => _EntriesListState();
|
_EntriesListState createState() => _EntriesListState();
|
||||||
@@ -63,14 +65,14 @@ class _EntriesListState extends State<EntriesList> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return FutureBuilder<List<Entry>>(
|
return FutureBuilder<List<Entry>>(
|
||||||
future: widget.api.list(widget.category.id),
|
future: widget.api.list(widget.category!.id!),
|
||||||
builder: (context, futureSnapshot) {
|
builder: (context, futureSnapshot) {
|
||||||
if (!futureSnapshot.hasData) {
|
if (!futureSnapshot.hasData) {
|
||||||
return _buildLoadingIndicator();
|
return _buildLoadingIndicator();
|
||||||
}
|
}
|
||||||
return StreamBuilder<List<Entry>>(
|
return StreamBuilder<List<Entry>>(
|
||||||
initialData: futureSnapshot.data,
|
initialData: futureSnapshot.data,
|
||||||
stream: widget.api.subscribe(widget.category.id),
|
stream: widget.api.subscribe(widget.category!.id!),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData) {
|
if (!snapshot.hasData) {
|
||||||
return _buildLoadingIndicator();
|
return _buildLoadingIndicator();
|
||||||
@@ -79,10 +81,10 @@ class _EntriesListState extends State<EntriesList> {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return EntryTile(
|
return EntryTile(
|
||||||
category: widget.category,
|
category: widget.category,
|
||||||
entry: snapshot.data[index],
|
entry: snapshot.data![index],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemCount: snapshot.data.length,
|
itemCount: snapshot.data!.length,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -96,20 +98,20 @@ class _EntriesListState extends State<EntriesList> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class EntryTile extends StatelessWidget {
|
class EntryTile extends StatelessWidget {
|
||||||
final Category category;
|
final Category? category;
|
||||||
final Entry entry;
|
final Entry? entry;
|
||||||
|
|
||||||
const EntryTile({
|
const EntryTile({
|
||||||
this.category,
|
this.category,
|
||||||
this.entry,
|
this.entry,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(entry.value.toString()),
|
title: Text(entry!.value.toString()),
|
||||||
subtitle: Text(intl.DateFormat('MM/dd/yy h:mm a').format(entry.time)),
|
subtitle: Text(intl.DateFormat('MM/dd/yy h:mm a').format(entry!.time)),
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
@@ -127,7 +129,7 @@ class EntryTile extends StatelessWidget {
|
|||||||
TextButton(
|
TextButton(
|
||||||
child: const Text('Delete'),
|
child: const Text('Delete'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
var shouldDelete = await showDialog<bool>(
|
var shouldDelete = await (showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: const Text('Delete entry?'),
|
title: const Text('Delete entry?'),
|
||||||
@@ -142,12 +144,12 @@ class EntryTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
) as FutureOr<bool>);
|
||||||
if (shouldDelete) {
|
if (shouldDelete) {
|
||||||
await Provider.of<AppState>(context, listen: false)
|
await Provider.of<AppState>(context, listen: false)
|
||||||
.api
|
.api!
|
||||||
.entries
|
.entries
|
||||||
.delete(category.id, entry.id);
|
.delete(category!.id!, entry!.id!);
|
||||||
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
// for details. All rights reserved. Use of this source code is governed by a
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../widgets/dialogs.dart';
|
import '../widgets/dialogs.dart';
|
||||||
@@ -13,8 +15,8 @@ class HomePage extends StatefulWidget {
|
|||||||
final VoidCallback onSignOut;
|
final VoidCallback onSignOut;
|
||||||
|
|
||||||
const HomePage({
|
const HomePage({
|
||||||
@required this.onSignOut,
|
required this.onSignOut,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -86,7 +88,7 @@ class _HomePageState extends State<HomePage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handleSignOut() async {
|
Future<void> _handleSignOut() async {
|
||||||
var shouldSignOut = await showDialog<bool>(
|
var shouldSignOut = await (showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: const Text('Are you sure you want to sign out?'),
|
title: const Text('Are you sure you want to sign out?'),
|
||||||
@@ -105,9 +107,9 @@ class _HomePageState extends State<HomePage> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
));
|
||||||
|
|
||||||
if (!shouldSignOut) {
|
if (shouldSignOut == null || !shouldSignOut) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ class SignInPage extends StatelessWidget {
|
|||||||
final ValueChanged<User> onSuccess;
|
final ValueChanged<User> onSuccess;
|
||||||
|
|
||||||
const SignInPage({
|
const SignInPage({
|
||||||
@required this.auth,
|
required this.auth,
|
||||||
@required this.onSuccess,
|
required this.onSuccess,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -31,9 +31,9 @@ class SignInButton extends StatefulWidget {
|
|||||||
final ValueChanged<User> onSuccess;
|
final ValueChanged<User> onSuccess;
|
||||||
|
|
||||||
const SignInButton({
|
const SignInButton({
|
||||||
@required this.auth,
|
required this.auth,
|
||||||
@required this.onSuccess,
|
required this.onSuccess,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -41,7 +41,7 @@ class SignInButton extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _SignInButtonState extends State<SignInButton> {
|
class _SignInButtonState extends State<SignInButton> {
|
||||||
Future<bool> _checkSignInFuture;
|
Future<bool>? _checkSignInFuture;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|||||||
@@ -15,14 +15,14 @@ class EntryTotal {
|
|||||||
|
|
||||||
/// Returns a list of [EntryTotal] objects. Each [EntryTotal] is the sum of
|
/// Returns a list of [EntryTotal] objects. Each [EntryTotal] is the sum of
|
||||||
/// the values of all the entries on a given day.
|
/// the values of all the entries on a given day.
|
||||||
List<EntryTotal> entryTotalsByDay(List<Entry> entries, int daysAgo,
|
List<EntryTotal> entryTotalsByDay(List<Entry>? entries, int daysAgo,
|
||||||
{DateTime today}) {
|
{DateTime? today}) {
|
||||||
today ??= DateTime.now();
|
today ??= DateTime.now();
|
||||||
return _entryTotalsByDay(entries, daysAgo, today).toList();
|
return _entryTotalsByDay(entries, daysAgo, today).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterable<EntryTotal> _entryTotalsByDay(
|
Iterable<EntryTotal> _entryTotalsByDay(
|
||||||
List<Entry> entries, int daysAgo, DateTime today) sync* {
|
List<Entry>? entries, int daysAgo, DateTime today) sync* {
|
||||||
var start = today.subtract(Duration(days: daysAgo));
|
var start = today.subtract(Duration(days: daysAgo));
|
||||||
var entriesByDay = _entriesInRange(start, today, entries);
|
var entriesByDay = _entriesInRange(start, today, entries);
|
||||||
|
|
||||||
@@ -42,18 +42,18 @@ Iterable<EntryTotal> _entryTotalsByDay(
|
|||||||
/// lists. The outer list represents the number of days since [start], and the
|
/// lists. The outer list represents the number of days since [start], and the
|
||||||
/// inner list is the group of entries on that day.
|
/// inner list is the group of entries on that day.
|
||||||
List<List<Entry>> _entriesInRange(
|
List<List<Entry>> _entriesInRange(
|
||||||
DateTime start, DateTime end, List<Entry> entries) =>
|
DateTime start, DateTime end, List<Entry>? entries) =>
|
||||||
_entriesInRangeImpl(start, end, entries).toList();
|
_entriesInRangeImpl(start, end, entries).toList();
|
||||||
|
|
||||||
Iterable<List<Entry>> _entriesInRangeImpl(
|
Iterable<List<Entry>> _entriesInRangeImpl(
|
||||||
DateTime start, DateTime end, List<Entry> entries) sync* {
|
DateTime start, DateTime end, List<Entry>? entries) sync* {
|
||||||
start = start.atMidnight;
|
start = start.atMidnight;
|
||||||
end = end.atMidnight;
|
end = end.atMidnight;
|
||||||
var d = start;
|
var d = start;
|
||||||
|
|
||||||
while (d.compareTo(end) <= 0) {
|
while (d.compareTo(end) <= 0) {
|
||||||
var es = <Entry>[];
|
var es = <Entry>[];
|
||||||
for (var entry in entries) {
|
for (var entry in entries!) {
|
||||||
if (d.isSameDay(entry.time.atMidnight)) {
|
if (d.isSameDay(entry.time.atMidnight)) {
|
||||||
es.add(entry);
|
es.add(entry);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ import '../api/api.dart';
|
|||||||
/// one.
|
/// one.
|
||||||
class CategoryDropdown extends StatefulWidget {
|
class CategoryDropdown extends StatefulWidget {
|
||||||
final CategoryApi api;
|
final CategoryApi api;
|
||||||
final ValueChanged<Category> onSelected;
|
final ValueChanged<Category?> onSelected;
|
||||||
|
|
||||||
const CategoryDropdown({
|
const CategoryDropdown({
|
||||||
@required this.api,
|
required this.api,
|
||||||
@required this.onSelected,
|
required this.onSelected,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -24,9 +24,9 @@ class CategoryDropdown extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _CategoryDropdownState extends State<CategoryDropdown> {
|
class _CategoryDropdownState extends State<CategoryDropdown> {
|
||||||
Category _selected;
|
Category? _selected;
|
||||||
Future<List<Category>> _future;
|
Future<List<Category>>? _future;
|
||||||
Stream<List<Category>> _stream;
|
Stream<List<Category>>? _stream;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -78,7 +78,7 @@ class _CategoryDropdownState extends State<CategoryDropdown> {
|
|||||||
initialData: futureSnapshot.hasData ? futureSnapshot.data : [],
|
initialData: futureSnapshot.hasData ? futureSnapshot.data : [],
|
||||||
stream: _stream,
|
stream: _stream,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
var data = snapshot.hasData ? snapshot.data : <Category>[];
|
var data = snapshot.hasData ? snapshot.data! : <Category>[];
|
||||||
return DropdownButton<Category>(
|
return DropdownButton<Category>(
|
||||||
value: _selected,
|
value: _selected,
|
||||||
items: data.map(_buildDropdownItem).toList(),
|
items: data.map(_buildDropdownItem).toList(),
|
||||||
@@ -92,7 +92,7 @@ class _CategoryDropdownState extends State<CategoryDropdown> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setSelected(Category category) {
|
void _setSelected(Category? category) {
|
||||||
if (_selected == category) {
|
if (_selected == category) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ const _daysBefore = 10;
|
|||||||
|
|
||||||
class CategoryChart extends StatelessWidget {
|
class CategoryChart extends StatelessWidget {
|
||||||
final Category category;
|
final Category category;
|
||||||
final DashboardApi api;
|
final DashboardApi? api;
|
||||||
|
|
||||||
const CategoryChart({
|
const CategoryChart({
|
||||||
@required this.category,
|
required this.category,
|
||||||
@required this.api,
|
required this.api,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -51,14 +51,14 @@ class CategoryChart extends StatelessWidget {
|
|||||||
// Load the initial snapshot using a FutureBuilder, and subscribe to
|
// Load the initial snapshot using a FutureBuilder, and subscribe to
|
||||||
// additional updates with a StreamBuilder.
|
// additional updates with a StreamBuilder.
|
||||||
child: FutureBuilder<List<Entry>>(
|
child: FutureBuilder<List<Entry>>(
|
||||||
future: api.entries.list(category.id),
|
future: api!.entries.list(category.id!),
|
||||||
builder: (context, futureSnapshot) {
|
builder: (context, futureSnapshot) {
|
||||||
if (!futureSnapshot.hasData) {
|
if (!futureSnapshot.hasData) {
|
||||||
return _buildLoadingIndicator();
|
return _buildLoadingIndicator();
|
||||||
}
|
}
|
||||||
return StreamBuilder<List<Entry>>(
|
return StreamBuilder<List<Entry>>(
|
||||||
initialData: futureSnapshot.data,
|
initialData: futureSnapshot.data,
|
||||||
stream: api.entries.subscribe(category.id),
|
stream: api!.entries.subscribe(category.id!),
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (!snapshot.hasData) {
|
if (!snapshot.hasData) {
|
||||||
return _buildLoadingIndicator();
|
return _buildLoadingIndicator();
|
||||||
@@ -74,12 +74,14 @@ class CategoryChart extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLoadingIndicator() {
|
Widget _buildLoadingIndicator() {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _BarChart extends StatelessWidget {
|
class _BarChart extends StatelessWidget {
|
||||||
final List<Entry> entries;
|
final List<Entry>? entries;
|
||||||
|
|
||||||
const _BarChart({this.entries});
|
const _BarChart({this.entries});
|
||||||
|
|
||||||
@@ -96,14 +98,10 @@ class _BarChart extends StatelessWidget {
|
|||||||
id: 'Entries',
|
id: 'Entries',
|
||||||
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
|
colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
|
||||||
domainFn: (entryTotal, _) {
|
domainFn: (entryTotal, _) {
|
||||||
if (entryTotal == null) return null;
|
|
||||||
|
|
||||||
var format = intl.DateFormat.Md();
|
var format = intl.DateFormat.Md();
|
||||||
return format.format(entryTotal.day);
|
return format.format(entryTotal.day);
|
||||||
},
|
},
|
||||||
measureFn: (total, _) {
|
measureFn: (total, _) {
|
||||||
if (total == null) return null;
|
|
||||||
|
|
||||||
return total.value;
|
return total.value;
|
||||||
},
|
},
|
||||||
data: utils.entryTotalsByDay(entries, _daysBefore),
|
data: utils.entryTotalsByDay(entries, _daysBefore),
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import 'package:web_dashboard/src/api/api.dart';
|
|||||||
import 'package:web_dashboard/src/app.dart';
|
import 'package:web_dashboard/src/app.dart';
|
||||||
|
|
||||||
class NewCategoryForm extends StatefulWidget {
|
class NewCategoryForm extends StatefulWidget {
|
||||||
const NewCategoryForm({Key key}) : super(key: key);
|
const NewCategoryForm({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_NewCategoryFormState createState() => _NewCategoryFormState();
|
_NewCategoryFormState createState() => _NewCategoryFormState();
|
||||||
@@ -24,7 +24,7 @@ class _NewCategoryFormState extends State<NewCategoryForm> {
|
|||||||
category: _category,
|
category: _category,
|
||||||
onDone: (shouldInsert) {
|
onDone: (shouldInsert) {
|
||||||
if (shouldInsert) {
|
if (shouldInsert) {
|
||||||
api.categories.insert(_category);
|
api!.categories.insert(_category);
|
||||||
}
|
}
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
@@ -37,9 +37,9 @@ class EditCategoryForm extends StatefulWidget {
|
|||||||
final ValueChanged<bool> onDone;
|
final ValueChanged<bool> onDone;
|
||||||
|
|
||||||
const EditCategoryForm({
|
const EditCategoryForm({
|
||||||
@required this.category,
|
required this.category,
|
||||||
@required this.onDone,
|
required this.onDone,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -67,7 +67,7 @@ class _EditCategoryFormState extends State<EditCategoryForm> {
|
|||||||
widget.category.name = newValue;
|
widget.category.name = newValue;
|
||||||
},
|
},
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value.isEmpty) {
|
if (value!.isEmpty) {
|
||||||
return 'Please enter a name';
|
return 'Please enter a name';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -91,7 +91,7 @@ class _EditCategoryFormState extends State<EditCategoryForm> {
|
|||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
child: const Text('OK'),
|
child: const Text('OK'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (_formKey.currentState.validate()) {
|
if (_formKey.currentState!.validate()) {
|
||||||
widget.onDone(true);
|
widget.onDone(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import '../app.dart';
|
|||||||
import 'edit_entry.dart';
|
import 'edit_entry.dart';
|
||||||
|
|
||||||
class NewCategoryDialog extends StatelessWidget {
|
class NewCategoryDialog extends StatelessWidget {
|
||||||
const NewCategoryDialog({Key key}) : super(key: key);
|
const NewCategoryDialog({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -28,8 +28,8 @@ class EditCategoryDialog extends StatelessWidget {
|
|||||||
final Category category;
|
final Category category;
|
||||||
|
|
||||||
const EditCategoryDialog({
|
const EditCategoryDialog({
|
||||||
@required this.category,
|
required this.category,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -43,7 +43,7 @@ class EditCategoryDialog extends StatelessWidget {
|
|||||||
category: category,
|
category: category,
|
||||||
onDone: (shouldUpdate) {
|
onDone: (shouldUpdate) {
|
||||||
if (shouldUpdate) {
|
if (shouldUpdate) {
|
||||||
api.categories.update(category, category.id);
|
api!.categories.update(category, category.id!);
|
||||||
}
|
}
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
@@ -54,7 +54,7 @@ class EditCategoryDialog extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NewEntryDialog extends StatefulWidget {
|
class NewEntryDialog extends StatefulWidget {
|
||||||
const NewEntryDialog({Key key}) : super(key: key);
|
const NewEntryDialog({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_NewEntryDialogState createState() => _NewEntryDialogState();
|
_NewEntryDialogState createState() => _NewEntryDialogState();
|
||||||
@@ -73,13 +73,13 @@ class _NewEntryDialogState extends State<NewEntryDialog> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class EditEntryDialog extends StatelessWidget {
|
class EditEntryDialog extends StatelessWidget {
|
||||||
final Category category;
|
final Category? category;
|
||||||
final Entry entry;
|
final Entry? entry;
|
||||||
|
|
||||||
const EditEntryDialog({
|
const EditEntryDialog({
|
||||||
this.category,
|
this.category,
|
||||||
this.entry,
|
this.entry,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -93,7 +93,7 @@ class EditEntryDialog extends StatelessWidget {
|
|||||||
entry: entry,
|
entry: entry,
|
||||||
onDone: (shouldUpdate) {
|
onDone: (shouldUpdate) {
|
||||||
if (shouldUpdate) {
|
if (shouldUpdate) {
|
||||||
api.entries.update(category.id, entry.id, entry);
|
api!.entries.update(category!.id!, entry!.id!, entry!);
|
||||||
}
|
}
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,19 +11,19 @@ import '../app.dart';
|
|||||||
import 'categories_dropdown.dart';
|
import 'categories_dropdown.dart';
|
||||||
|
|
||||||
class NewEntryForm extends StatefulWidget {
|
class NewEntryForm extends StatefulWidget {
|
||||||
const NewEntryForm({Key key}) : super(key: key);
|
const NewEntryForm({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_NewEntryFormState createState() => _NewEntryFormState();
|
_NewEntryFormState createState() => _NewEntryFormState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NewEntryFormState extends State<NewEntryForm> {
|
class _NewEntryFormState extends State<NewEntryForm> {
|
||||||
Category _selected;
|
late Category _selected;
|
||||||
final Entry _entry = Entry(0, DateTime.now());
|
final Entry _entry = Entry(0, DateTime.now());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var api = Provider.of<AppState>(context).api;
|
var api = Provider.of<AppState>(context).api!;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -33,6 +33,7 @@ class _NewEntryFormState extends State<NewEntryForm> {
|
|||||||
child: CategoryDropdown(
|
child: CategoryDropdown(
|
||||||
api: api.categories,
|
api: api.categories,
|
||||||
onSelected: (category) {
|
onSelected: (category) {
|
||||||
|
if (category == null) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_selected = category;
|
_selected = category;
|
||||||
});
|
});
|
||||||
@@ -43,7 +44,7 @@ class _NewEntryFormState extends State<NewEntryForm> {
|
|||||||
entry: _entry,
|
entry: _entry,
|
||||||
onDone: (shouldInsert) {
|
onDone: (shouldInsert) {
|
||||||
if (shouldInsert) {
|
if (shouldInsert) {
|
||||||
api.entries.insert(_selected.id, _entry);
|
api.entries.insert(_selected.id!, _entry);
|
||||||
}
|
}
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
@@ -54,13 +55,13 @@ class _NewEntryFormState extends State<NewEntryForm> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class EditEntryForm extends StatefulWidget {
|
class EditEntryForm extends StatefulWidget {
|
||||||
final Entry entry;
|
final Entry? entry;
|
||||||
final ValueChanged<bool> onDone;
|
final ValueChanged<bool> onDone;
|
||||||
|
|
||||||
const EditEntryForm({
|
const EditEntryForm({
|
||||||
@required this.entry,
|
required this.entry,
|
||||||
@required this.onDone,
|
required this.onDone,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -80,19 +81,19 @@ class _EditEntryFormState extends State<EditEntryForm> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
initialValue: widget.entry.value.toString(),
|
initialValue: widget.entry!.value.toString(),
|
||||||
decoration: const InputDecoration(labelText: 'Value'),
|
decoration: const InputDecoration(labelText: 'Value'),
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
try {
|
try {
|
||||||
int.parse(value);
|
int.parse(value!);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return "Please enter a whole number";
|
return "Please enter a whole number";
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
onChanged: (newValue) {
|
onChanged: (newValue) {
|
||||||
widget.entry.value = int.parse(newValue);
|
widget.entry!.value = int.parse(newValue);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -101,13 +102,13 @@ class _EditEntryFormState extends State<EditEntryForm> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(intl.DateFormat('MM/dd/yyyy').format(widget.entry.time)),
|
Text(intl.DateFormat('MM/dd/yyyy').format(widget.entry!.time)),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
child: const Text('Edit'),
|
child: const Text('Edit'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
var result = await showDatePicker(
|
var result = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: widget.entry.time,
|
initialDate: widget.entry!.time,
|
||||||
firstDate:
|
firstDate:
|
||||||
DateTime.now().subtract(const Duration(days: 365)),
|
DateTime.now().subtract(const Duration(days: 365)),
|
||||||
lastDate: DateTime.now());
|
lastDate: DateTime.now());
|
||||||
@@ -115,7 +116,7 @@ class _EditEntryFormState extends State<EditEntryForm> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
widget.entry.time = result;
|
widget.entry!.time = result;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -139,7 +140,7 @@ class _EditEntryFormState extends State<EditEntryForm> {
|
|||||||
child: ElevatedButton(
|
child: ElevatedButton(
|
||||||
child: const Text('OK'),
|
child: const Text('OK'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (_formKey.currentState.validate()) {
|
if (_formKey.currentState!.validate()) {
|
||||||
widget.onDone(true);
|
widget.onDone(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ class AdaptiveScaffoldDestination {
|
|||||||
final IconData icon;
|
final IconData icon;
|
||||||
|
|
||||||
const AdaptiveScaffoldDestination({
|
const AdaptiveScaffoldDestination({
|
||||||
@required this.title,
|
required this.title,
|
||||||
@required this.icon,
|
required this.icon,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,23 +27,23 @@ class AdaptiveScaffoldDestination {
|
|||||||
/// [NavigationRail], or [BottomNavigationBar]. Navigation destinations are
|
/// [NavigationRail], or [BottomNavigationBar]. Navigation destinations are
|
||||||
/// defined in the [destinations] parameter.
|
/// defined in the [destinations] parameter.
|
||||||
class AdaptiveScaffold extends StatefulWidget {
|
class AdaptiveScaffold extends StatefulWidget {
|
||||||
final Widget title;
|
final Widget? title;
|
||||||
final List<Widget> actions;
|
final List<Widget> actions;
|
||||||
final Widget body;
|
final Widget? body;
|
||||||
final int currentIndex;
|
final int currentIndex;
|
||||||
final List<AdaptiveScaffoldDestination> destinations;
|
final List<AdaptiveScaffoldDestination> destinations;
|
||||||
final ValueChanged<int> onNavigationIndexChange;
|
final ValueChanged<int>? onNavigationIndexChange;
|
||||||
final FloatingActionButton floatingActionButton;
|
final FloatingActionButton? floatingActionButton;
|
||||||
|
|
||||||
const AdaptiveScaffold({
|
const AdaptiveScaffold({
|
||||||
this.title,
|
this.title,
|
||||||
this.body,
|
this.body,
|
||||||
this.actions = const [],
|
this.actions = const [],
|
||||||
@required this.currentIndex,
|
required this.currentIndex,
|
||||||
@required this.destinations,
|
required this.destinations,
|
||||||
this.onNavigationIndexChange,
|
this.onNavigationIndexChange,
|
||||||
this.floatingActionButton,
|
this.floatingActionButton,
|
||||||
Key key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -122,7 +122,7 @@ class _AdaptiveScaffoldState extends State<AdaptiveScaffold> {
|
|||||||
color: Colors.grey[300],
|
color: Colors.grey[300],
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: widget.body,
|
child: widget.body!,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -155,7 +155,7 @@ class _AdaptiveScaffoldState extends State<AdaptiveScaffold> {
|
|||||||
void _destinationTapped(AdaptiveScaffoldDestination destination) {
|
void _destinationTapped(AdaptiveScaffoldDestination destination) {
|
||||||
var idx = widget.destinations.indexOf(destination);
|
var idx = widget.destinations.indexOf(destination);
|
||||||
if (idx != widget.currentIndex) {
|
if (idx != widget.currentIndex) {
|
||||||
widget.onNavigationIndexChange(idx);
|
widget.onNavigationIndexChange!(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ packages:
|
|||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.0.0"
|
version: "28.0.0"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.41.2"
|
version: "2.5.0"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -28,7 +28,7 @@ packages:
|
|||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.8.1"
|
version: "2.8.2"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -42,42 +42,42 @@ packages:
|
|||||||
name: build
|
name: build
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.6.2"
|
version: "2.1.1"
|
||||||
build_config:
|
build_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_config
|
name: build_config
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.5"
|
version: "1.0.0"
|
||||||
build_daemon:
|
build_daemon:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_daemon
|
name: build_daemon
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.10"
|
version: "3.0.1"
|
||||||
build_resolvers:
|
build_resolvers:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.5.3"
|
version: "2.0.4"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: build_runner
|
name: build_runner
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.11.1+1"
|
version: "2.1.4"
|
||||||
build_runner_core:
|
build_runner_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_runner_core
|
name: build_runner_core
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.7"
|
version: "7.2.2"
|
||||||
built_collection:
|
built_collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -98,7 +98,7 @@ packages:
|
|||||||
name: characters
|
name: characters
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.2.0"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -117,8 +117,8 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: charts_flutter
|
path: charts_flutter
|
||||||
ref: HEAD
|
ref: "03c2aa67d12952e7f3cbba1d87646d96a9a7cdc4"
|
||||||
resolved-ref: "30477090290b348ed3101bc13017aae465f59017"
|
resolved-ref: "03c2aa67d12952e7f3cbba1d87646d96a9a7cdc4"
|
||||||
url: "git://github.com/google/charts.git"
|
url: "git://github.com/google/charts.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.11.0"
|
version: "0.11.0"
|
||||||
@@ -128,7 +128,7 @@ packages:
|
|||||||
name: checked_yaml
|
name: checked_yaml
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.4"
|
version: "2.0.1"
|
||||||
cli_util:
|
cli_util:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -149,28 +149,28 @@ packages:
|
|||||||
name: cloud_firestore
|
name: cloud_firestore
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.7"
|
version: "2.5.3"
|
||||||
cloud_firestore_platform_interface:
|
cloud_firestore_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cloud_firestore_platform_interface
|
name: cloud_firestore_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.2"
|
version: "5.4.2"
|
||||||
cloud_firestore_web:
|
cloud_firestore_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: cloud_firestore_web
|
name: cloud_firestore_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.1+2"
|
version: "2.4.3"
|
||||||
code_builder:
|
code_builder:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: code_builder
|
name: code_builder
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.7.0"
|
version: "4.1.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -184,28 +184,28 @@ packages:
|
|||||||
name: convert
|
name: convert
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "3.0.1"
|
||||||
crypto:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.5"
|
version: "3.0.1"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: cupertino_icons
|
name: cupertino_icons
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.3"
|
version: "1.0.3"
|
||||||
dart_style:
|
dart_style:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: dart_style
|
name: dart_style
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.12"
|
version: "2.2.0"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -219,56 +219,49 @@ packages:
|
|||||||
name: file
|
name: file
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.2.1"
|
version: "6.1.2"
|
||||||
firebase:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: firebase
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "7.3.3"
|
|
||||||
firebase_auth:
|
firebase_auth:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_auth
|
name: firebase_auth
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.16.1"
|
version: "3.1.3"
|
||||||
firebase_auth_platform_interface:
|
firebase_auth_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_auth_platform_interface
|
name: firebase_auth_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.8"
|
version: "6.1.1"
|
||||||
firebase_auth_web:
|
firebase_auth_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_auth_web
|
name: firebase_auth_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.3+1"
|
version: "3.1.2"
|
||||||
firebase_core:
|
firebase_core:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: firebase_core
|
name: firebase_core
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.4"
|
version: "1.7.0"
|
||||||
firebase_core_platform_interface:
|
firebase_core_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_core_platform_interface
|
name: firebase_core_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "4.0.1"
|
||||||
firebase_core_web:
|
firebase_core_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: firebase_core_web
|
name: firebase_core_web
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.1+2"
|
version: "1.1.0"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -298,13 +291,20 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
frontend_server_client:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: frontend_server_client
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
glob:
|
glob:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: glob
|
name: glob
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "2.0.2"
|
||||||
google_sign_in:
|
google_sign_in:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -332,35 +332,28 @@ packages:
|
|||||||
name: graphs
|
name: graphs
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "2.1.0"
|
||||||
grinder:
|
grinder:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: grinder
|
name: grinder
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.8.6"
|
version: "0.9.0"
|
||||||
http:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: http
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "0.12.2"
|
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http_multi_server
|
name: http_multi_server
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "3.0.1"
|
||||||
http_parser:
|
http_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: http_parser
|
name: http_parser
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.4"
|
version: "4.0.0"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -374,7 +367,7 @@ packages:
|
|||||||
name: io
|
name: io
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.5"
|
version: "1.0.3"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -388,14 +381,14 @@ packages:
|
|||||||
name: json_annotation
|
name: json_annotation
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.1"
|
version: "4.1.0"
|
||||||
json_serializable:
|
json_serializable:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: json_serializable
|
name: json_serializable
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.5.1"
|
version: "5.0.2"
|
||||||
lints:
|
lints:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -416,7 +409,7 @@ packages:
|
|||||||
name: matcher
|
name: matcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.10"
|
version: "0.12.11"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -438,27 +431,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
node_interop:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: node_interop
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.2.1"
|
|
||||||
node_io:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: node_io
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.2.0"
|
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_config
|
name: package_config
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.3"
|
version: "2.0.2"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -466,20 +445,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.8.0"
|
||||||
pedantic:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: pedantic
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.11.1"
|
|
||||||
plugin_platform_interface:
|
plugin_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: plugin_platform_interface
|
name: plugin_platform_interface
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "2.0.2"
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -493,7 +465,7 @@ packages:
|
|||||||
name: provider
|
name: provider
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.3.3"
|
version: "6.0.1"
|
||||||
pub_semver:
|
pub_semver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -507,7 +479,7 @@ packages:
|
|||||||
name: pubspec_parse
|
name: pubspec_parse
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.8"
|
version: "1.1.0"
|
||||||
quiver:
|
quiver:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -515,27 +487,20 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.1"
|
version: "3.0.1"
|
||||||
quiver_hashcode:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: quiver_hashcode
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.0"
|
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf
|
name: shelf
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.9"
|
version: "1.2.0"
|
||||||
shelf_web_socket:
|
shelf_web_socket:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shelf_web_socket
|
name: shelf_web_socket
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.4+1"
|
version: "1.0.1"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -547,7 +512,14 @@ packages:
|
|||||||
name: source_gen
|
name: source_gen
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.10+3"
|
version: "1.1.1"
|
||||||
|
source_helper:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: source_helper
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.0"
|
||||||
source_span:
|
source_span:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -596,14 +568,14 @@ packages:
|
|||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.2"
|
version: "0.4.3"
|
||||||
timing:
|
timing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: timing
|
name: timing
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.1+3"
|
version: "1.0.0"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -617,14 +589,14 @@ packages:
|
|||||||
name: uuid
|
name: uuid
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.2"
|
version: "3.0.5"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_math
|
name: vector_math
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.1"
|
||||||
watcher:
|
watcher:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -638,7 +610,7 @@ packages:
|
|||||||
name: web_socket_channel
|
name: web_socket_channel
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "2.1.0"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -3,28 +3,31 @@ description: A dashboard app sample
|
|||||||
version: 1.0.0+1
|
version: 1.0.0+1
|
||||||
publish_to: none
|
publish_to: none
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.6.0 <3.0.0"
|
sdk: '>=2.12.0 <3.0.0'
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
cloud_firestore: ^0.13.0
|
cloud_firestore: ^2.5.0
|
||||||
cupertino_icons: ^0.1.2
|
cupertino_icons: ^1.0.0
|
||||||
firebase_auth: ^0.16.1
|
firebase_auth: ^3.1.0
|
||||||
firebase_core: ^0.4.3
|
firebase_core: ^1.7.0
|
||||||
google_sign_in: ^5.0.0
|
google_sign_in: ^5.0.0
|
||||||
json_annotation: ^3.0.0
|
json_annotation: ^4.1.0
|
||||||
provider: ^4.0.0
|
provider: ^6.0.0
|
||||||
uuid: ^2.0.0
|
uuid: ^3.0.0
|
||||||
|
# The published version of charts_flutter is not up to date with master:
|
||||||
|
# https://github.com/google/charts/issues/678
|
||||||
charts_flutter:
|
charts_flutter:
|
||||||
git:
|
git:
|
||||||
url: git://github.com/google/charts.git
|
url: git://github.com/google/charts.git
|
||||||
path: charts_flutter
|
path: charts_flutter
|
||||||
|
ref: 03c2aa67d12952e7f3cbba1d87646d96a9a7cdc4
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
build_runner: ^1.8.0
|
build_runner: ^2.1.0
|
||||||
json_serializable: ^3.3.0
|
json_serializable: ^5.0.0
|
||||||
grinder: ^0.8.4
|
grinder: ^0.9.0
|
||||||
flutter_lints: ^1.0.0
|
flutter_lints: ^1.0.0
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import 'package:web_dashboard/src/api/mock.dart';
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('mock dashboard API', () {
|
group('mock dashboard API', () {
|
||||||
DashboardApi api;
|
late DashboardApi api;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
api = MockDashboardApi();
|
api = MockDashboardApi();
|
||||||
@@ -24,9 +24,10 @@ void main() {
|
|||||||
test('delete', () async {
|
test('delete', () async {
|
||||||
await api.categories.insert(Category('Coffees Drank'));
|
await api.categories.insert(Category('Coffees Drank'));
|
||||||
var category = await api.categories.insert(Category('Miles Ran'));
|
var category = await api.categories.insert(Category('Miles Ran'));
|
||||||
var removed = await api.categories.delete(category.id);
|
var removed = await api.categories.delete(category.id!);
|
||||||
|
|
||||||
expect(removed.name, 'Miles Ran');
|
expect(removed, isNotNull);
|
||||||
|
expect(removed!.name, 'Miles Ran');
|
||||||
|
|
||||||
var categories = await api.categories.list();
|
var categories = await api.categories.list();
|
||||||
expect(categories, hasLength(1));
|
expect(categories, hasLength(1));
|
||||||
@@ -34,10 +35,11 @@ void main() {
|
|||||||
|
|
||||||
test('update', () async {
|
test('update', () async {
|
||||||
var category = await api.categories.insert(Category('Coffees Drank'));
|
var category = await api.categories.insert(Category('Coffees Drank'));
|
||||||
await api.categories.update(Category('Bagels Consumed'), category.id);
|
await api.categories.update(Category('Bagels Consumed'), category.id!);
|
||||||
|
|
||||||
var latest = await api.categories.get(category.id);
|
var latest = await api.categories.get(category.id!);
|
||||||
expect(latest.name, equals('Bagels Consumed'));
|
expect(latest, isNotNull);
|
||||||
|
expect(latest!.name, equals('Bagels Consumed'));
|
||||||
});
|
});
|
||||||
test('subscribe', () async {
|
test('subscribe', () async {
|
||||||
var stream = api.categories.subscribe();
|
var stream = api.categories.subscribe();
|
||||||
@@ -51,7 +53,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group('entry service', () {
|
group('entry service', () {
|
||||||
Category category;
|
late Category category;
|
||||||
DateTime dateTime = DateTime(2020, 1, 1, 30, 45);
|
DateTime dateTime = DateTime(2020, 1, 1, 30, 45);
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
@@ -60,38 +62,38 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('insert', () async {
|
test('insert', () async {
|
||||||
var entry = await api.entries.insert(category.id, Entry(1, dateTime));
|
var entry = await api.entries.insert(category.id!, Entry(1, dateTime));
|
||||||
|
|
||||||
expect(entry.value, 1);
|
expect(entry.value, 1);
|
||||||
expect(entry.time, dateTime);
|
expect(entry.time, dateTime);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('delete', () async {
|
test('delete', () async {
|
||||||
await api.entries.insert(category.id, Entry(1, dateTime));
|
await api.entries.insert(category.id!, Entry(1, dateTime));
|
||||||
var entry2 = await api.entries.insert(category.id, Entry(2, dateTime));
|
var entry2 = await api.entries.insert(category.id!, Entry(2, dateTime));
|
||||||
|
|
||||||
await api.entries.delete(category.id, entry2.id);
|
await api.entries.delete(category.id!, entry2.id!);
|
||||||
|
|
||||||
var entries = await api.entries.list(category.id);
|
var entries = await api.entries.list(category.id!);
|
||||||
expect(entries, hasLength(1));
|
expect(entries, hasLength(1));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('update', () async {
|
test('update', () async {
|
||||||
var entry = await api.entries.insert(category.id, Entry(1, dateTime));
|
var entry = await api.entries.insert(category.id!, Entry(1, dateTime));
|
||||||
var updated =
|
var updated = await api.entries
|
||||||
await api.entries.update(category.id, entry.id, Entry(2, dateTime));
|
.update(category.id!, entry.id!, Entry(2, dateTime));
|
||||||
expect(updated.value, 2);
|
expect(updated.value, 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('subscribe', () async {
|
test('subscribe', () async {
|
||||||
var stream = api.entries.subscribe(category.id);
|
var stream = api.entries.subscribe(category.id!);
|
||||||
|
|
||||||
stream.listen(expectAsync1((x) {
|
stream.listen(expectAsync1((x) {
|
||||||
expect(x, hasLength(1));
|
expect(x, hasLength(1));
|
||||||
expect(x.first.value, equals(1));
|
expect(x.first.value, equals(1));
|
||||||
}, count: 1));
|
}, count: 1));
|
||||||
|
|
||||||
await api.entries.insert(category.id, Entry(1, dateTime));
|
await api.entries.insert(category.id!, Entry(1, dateTime));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user