rename roster anf fix search functionality

This commit is contained in:
Aaron Fischer 2019-07-16 23:37:18 +02:00
parent 51196f7c8c
commit 5068592189
9 changed files with 200 additions and 114 deletions

View file

@ -3,39 +3,40 @@ import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:toolheim/data/warband_roaster.dart'; import 'package:preferences/preferences.dart';
import 'package:toolheim/data/warband_roster.dart';
import 'package:yaml/yaml.dart'; import 'package:yaml/yaml.dart';
class GitHubAdapter extends ChangeNotifier { class GitHubAdapter extends ChangeNotifier {
String _repository = 'Labernator/Mordheim';
String _path = 'Mordheim-BorderTownBurning/Warband Rosters';
List<String> _syncErrors = new List<String>(); List<String> _syncErrors = new List<String>();
DateTime _lastSync; DateTime _lastSync;
List<WarbandRoaster> _roasters = []; List<WarbandRoster> _rosters = [];
String _activePlayerName; String _activePlayerName;
String get repository => _repository; String get repository => PrefService.getString('repository');
String get path => _path; String get path => PrefService.getString('path');
bool _syncinProgress = false;
bool get isSyncInProgress => _syncinProgress;
DateTime get lastSync => _lastSync; DateTime get lastSync => _lastSync;
UnmodifiableListView<String> get syncErrors => _syncErrors; List<String> get syncErrors => _syncErrors;
UnmodifiableListView<WarbandRoaster> get roasters => UnmodifiableListView<WarbandRoster> get rosters =>
UnmodifiableListView(_roasters); UnmodifiableListView(_rosters);
WarbandRoaster activeRoaster() { WarbandRoster activeRoster() {
if (_activePlayerName == null || _roasters.length == 0) { if (_activePlayerName == null || _rosters.length == 0) {
return null; return null;
} }
return _roasters.firstWhere((roaster) { return _rosters.firstWhere((roster) {
return roaster.playerName == _activePlayerName; return roster.playerName == _activePlayerName;
}); }, orElse: () => null);
} }
void changeActiveRoaster(String playerName) { void changeActiveRoster(String playerName) {
_activePlayerName = playerName; _activePlayerName = playerName;
notifyListeners(); notifyListeners();
} }
@ -50,20 +51,21 @@ class GitHubAdapter extends ChangeNotifier {
/// the [syncErrors] list. /// the [syncErrors] list.
void search() async { void search() async {
_syncErrors.clear(); _syncErrors.clear();
_syncinProgress = true;
Stream<Map<String, String>> roasterStream() async* { Stream<Map<String, String>> rosterStream() async* {
// Get all files which could be potential warband files (end with // Get all files which could be potential warband files (end with
// mordheim.yml and contain the word "heros"). // mordheim.yml and contain the word "heros").
http.Response response = await http.get( http.Response response = await http.get(
"https://api.github.com/search/code?q=heros+repo:" + "https://api.github.com/search/code?q=heros+repo:" +
_repository + repository +
"+filename:mordheim.yml+path:\"" + "+filename:mordheim.yml+path:\"" +
_path + path +
"\""); "\"");
// GitHub is not reachable // GitHub is not reachable
if (response.statusCode != 200) { if (response.statusCode != 200) {
_syncErrors.add('Could not find any warband roaster files'); _syncErrors.add('Could not find any warband roster files.');
yield {}; yield {};
return; return;
} }
@ -73,7 +75,7 @@ class GitHubAdapter extends ChangeNotifier {
try { try {
searchResults = jsonDecode(response.body); searchResults = jsonDecode(response.body);
} on FormatException catch (e) { } on FormatException catch (e) {
_syncErrors.add('Could not parse GitHub response.' + e.toString()); _syncErrors.add('Could not parse GitHub response. ' + e.toString());
yield {}; yield {};
return; return;
} }
@ -86,7 +88,7 @@ class GitHubAdapter extends ChangeNotifier {
// in which the file resists // in which the file resists
String completePath = searchResult['path']; String completePath = searchResult['path'];
List<String> pathParts = List<String> pathParts =
completePath.substring(_path.length + 1).split('/'); completePath.substring(path.length + 1).split('/');
String playerName; String playerName;
if (pathParts.length >= 2) { if (pathParts.length >= 2) {
@ -96,7 +98,7 @@ class GitHubAdapter extends ChangeNotifier {
// Fetch last change and some metainformation of the file // Fetch last change and some metainformation of the file
http.Response response = await http.get( http.Response response = await http.get(
"https://api.github.com/repos/" + "https://api.github.com/repos/" +
_repository + repository +
"/commits?path=" + "/commits?path=" +
completePath); completePath);
@ -110,7 +112,7 @@ class GitHubAdapter extends ChangeNotifier {
try { try {
commits = jsonDecode(response.body); commits = jsonDecode(response.body);
} on FormatException catch (e) { } on FormatException catch (e) {
_syncErrors.add('Could not parse GitHub response.' + e.toString()); _syncErrors.add('Could not parse GitHub response. ' + e.toString());
continue; continue;
} }
@ -132,52 +134,66 @@ class GitHubAdapter extends ChangeNotifier {
} }
} }
_roasters.clear(); _rosters.clear();
notifyListeners(); notifyListeners();
await for (Map<String, String> player in roasterStream()) {
http.Response response = await http.get( if (_syncErrors.length == 0) {
"https://raw.githubusercontent.com/" + await for (Map<String, String> player in rosterStream()) {
_repository + http.Response response;
try {
response = await http.get("https://raw.githubusercontent.com/" +
repository +
'/master/' + '/master/' +
player['filePath']); player['filePath']);
} catch (e) {
try { // TODO: Ignore this error, we catch it elsewhere.
YamlMap yamlObject = loadYaml(response.body);
WarbandRoaster roaster = WarbandRoaster.fromJson(yamlObject);
if (player['player'] != '') {
roaster.playerName = player['player'];
} }
roaster.currentVersion = new Version(player['shaHash'], player['date'], try {
player['author'], player['message']); if (response != null) {
YamlMap yamlObject = loadYaml(response.body);
WarbandRoster roster = WarbandRoster.fromJson(yamlObject);
if (player['player'] != '') {
roster.playerName = player['player'];
}
// On a search, we drop all previous information about the warbands, roster.currentVersion = new Version(player['shaHash'],
// Sp, lastSyncVersion is equal to the currentVersion. player['date'], player['author'], player['message']);
roaster.lastSyncVersion = roaster.currentVersion;
_roasters.add(roaster); // On a search, we drop all previous information about the warbands,
notifyListeners(); // Sp, lastSyncVersion is equal to the currentVersion.
} catch (e) { roster.lastSyncVersion = roster.currentVersion;
_syncErrors.add(e.toString());
_rosters.add(roster);
notifyListeners();
}
} catch (e) {
_syncErrors.add(e.toString());
}
} }
} }
// Sort by CP // Sort by CP
_roasters.sort((a, b) => b.campaignPoints.compareTo(a.campaignPoints)); _rosters.sort((a, b) => b.campaignPoints.compareTo(a.campaignPoints));
// Select first as active player if no active player is selected // Select first as active player if no active player is selected
_activePlayerName = _roasters.first.playerName; if (_rosters.length > 0) {
_activePlayerName = _rosters.first.playerName;
}
_lastSync = DateTime.now(); _lastSync = DateTime.now();
_syncinProgress = false;
notifyListeners(); notifyListeners();
} }
void update() async { void update() async {
_syncinProgress = true;
// TODO: Search for warband yml files // TODO: Search for warband yml files
// TODO: Check if it is in the right format // TODO: Check if it is in the right format
// TODO: Store it into the database if valid // TODO: Store it into the database if valid
_lastSync = DateTime.now(); _lastSync = DateTime.now();
_syncinProgress = false;
notifyListeners(); notifyListeners();
} }
} }

View file

@ -3,7 +3,7 @@ import 'dart:collection';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
part 'warband_roaster.g.dart'; part 'warband_roster.g.dart';
// flutter packages pub run build_runner build --delete-conflicting-outputs // flutter packages pub run build_runner build --delete-conflicting-outputs
@ -166,7 +166,7 @@ class Version {
} }
@JsonSerializable(nullable: true, anyMap: true, createToJson: false) @JsonSerializable(nullable: true, anyMap: true, createToJson: false)
class WarbandRoaster { class WarbandRoster {
/// Store the complete string of name and race. This will split up into the /// Store the complete string of name and race. This will split up into the
/// fields name and race. /// fields name and race.
@JsonKey(name: 'warband', fromJson: _warbandNameAndRace) @JsonKey(name: 'warband', fromJson: _warbandNameAndRace)
@ -203,7 +203,7 @@ class WarbandRoaster {
@JsonKey(ignore: true) @JsonKey(ignore: true)
Version currentVersion; Version currentVersion;
WarbandRoaster( WarbandRoster(
this.nameAndRace, this.nameAndRace,
this.campaignPoints, this.campaignPoints,
this.objective, this.objective,
@ -234,5 +234,5 @@ class WarbandRoaster {
return nr; return nr;
} }
factory WarbandRoaster.fromJson(yaml) => _$WarbandRoasterFromJson(yaml); factory WarbandRoster.fromJson(yaml) => _$WarbandRosterFromJson(yaml);
} }

View file

@ -1,6 +1,6 @@
// GENERATED CODE - DO NOT MODIFY BY HAND // GENERATED CODE - DO NOT MODIFY BY HAND
part of 'warband_roaster.dart'; part of 'warband_roster.dart';
// ************************************************************************** // **************************************************************************
// JsonSerializableGenerator // JsonSerializableGenerator
@ -25,9 +25,9 @@ Hero _$HeroFromJson(Map json) {
Hero._heroHeaderFromJson(json['hero'] as String)); Hero._heroHeaderFromJson(json['hero'] as String));
} }
WarbandRoaster _$WarbandRoasterFromJson(Map json) { WarbandRoster _$WarbandRosterFromJson(Map json) {
return WarbandRoaster( return WarbandRoster(
WarbandRoaster._warbandNameAndRace(json['warband'] as String), WarbandRoster._warbandNameAndRace(json['warband'] as String),
json['campaign'] as int, json['campaign'] as int,
json['objective'] as String, json['objective'] as String,
json['alignment'] as String, json['alignment'] as String,

View file

@ -1,10 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:preferences/preferences.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:toolheim/data/github_adapter.dart'; import 'package:toolheim/data/github_adapter.dart';
import 'package:toolheim/screens/settings_screen.dart'; import 'package:toolheim/screens/settings_screen.dart';
import 'package:toolheim/screens/warband_roaster_screen.dart'; import 'package:toolheim/screens/warband_roster_screen.dart';
void main() { void main() async {
await PrefService.init(prefix: 'toolheim_');
runApp(Toolheim()); runApp(Toolheim());
} }
@ -21,7 +23,7 @@ class Toolheim extends StatelessWidget {
), ),
initialRoute: '/', initialRoute: '/',
routes: { routes: {
'/': (context) => WarbandRoasterScreen(), '/': (context) => WarbandRosterScreen(),
'/settings': (context) => SettingsScreen() '/settings': (context) => SettingsScreen()
}, },
), ),

View file

@ -1,26 +1,22 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:preferences/preferences.dart';
import 'package:toolheim/data/github_adapter.dart';
class SettingsScreen extends StatelessWidget { class SettingsScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
GitHubAdapter github = Provider.of<GitHubAdapter>(context);
// TODO: Add this to local storage
//String _repository = 'Labernator/Mordheim';
//String _path = 'Mordheim-BorderTownBurning/Warband Rosters';
return Scaffold( return Scaffold(
appBar: AppBar(title: Text('Settings')), appBar: AppBar(title: Text('Settings')),
body: Column(children: <Widget>[ body: PreferencePage([
TextField( PreferenceTitle('GitHub'),
decoration: InputDecoration(labelText: 'Repository'), PreferenceText(
), 'Provide a valid GitHub repository with username and project (username/repository-name). This repository must contain warband roster files in subfolders. See the sample project for a kickstart.'),
TextField( TextFieldPreference('Repository', 'repository'),
decoration: InputDecoration(labelText: 'Path'), PreferenceText(
) 'If your warband folders are placed in a subfolder, you can specify it here.'),
TextFieldPreference('Path', 'path', defaultVal: '/')
])); ]));
//String _repository = 'Labernator/Mordheim';
//String _path = 'Mordheim-BorderTownBurning/Warband Rosters';
} }
} }

View file

@ -2,16 +2,16 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:toolheim/data/github_adapter.dart'; import 'package:toolheim/data/github_adapter.dart';
import 'package:toolheim/data/warband_roaster.dart'; import 'package:toolheim/data/warband_roster.dart';
import 'package:toolheim/widgets/warband_drawer_widget.dart'; import 'package:toolheim/widgets/warband_drawer_widget.dart';
class WarbandRoasterScreen extends StatelessWidget { class WarbandRosterScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
GitHubAdapter github = Provider.of<GitHubAdapter>(context); GitHubAdapter github = Provider.of<GitHubAdapter>(context);
WarbandRoaster roaster = github.activeRoaster(); WarbandRoster roster = github.activeRoster();
if (roaster == null) { if (roster == null) {
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('Toolheim')), appBar: AppBar(title: const Text('Toolheim')),
body: Builder(builder: (BuildContext context) { body: Builder(builder: (BuildContext context) {
@ -41,7 +41,7 @@ class WarbandRoasterScreen extends StatelessWidget {
List<Widget> tiles = new List(); List<Widget> tiles = new List();
roaster.heros.forEach((hero) { roster.heros.forEach((hero) {
tiles.add(new ListTile( tiles.add(new ListTile(
title: Text(hero.name), title: Text(hero.name),
leading: CircleAvatar( leading: CircleAvatar(
@ -55,7 +55,7 @@ class WarbandRoasterScreen extends StatelessWidget {
tiles.add(Divider()); tiles.add(Divider());
roaster.henchmenGroups.forEach((henchmenGroup) { roster.henchmenGroups.forEach((henchmenGroup) {
tiles.add(new ListTile( tiles.add(new ListTile(
title: Text(henchmenGroup.name), title: Text(henchmenGroup.name),
trailing: Chip(label: Text(henchmenGroup.number.toString() + 'x')), trailing: Chip(label: Text(henchmenGroup.number.toString() + 'x')),
@ -70,7 +70,7 @@ class WarbandRoasterScreen extends StatelessWidget {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(roaster.name), title: Text(roster.name),
), ),
drawer: drawer:
Drawer(child: SingleChildScrollView(child: WarbandDrawerWidget())), Drawer(child: SingleChildScrollView(child: WarbandDrawerWidget())),

View file

@ -1,18 +1,18 @@
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:badges/badges.dart'; import 'package:badges/badges.dart';
import 'package:toolheim/data/github_adapter.dart'; import 'package:toolheim/data/github_adapter.dart';
import 'package:toolheim/data/warband_roaster.dart'; import 'package:toolheim/data/warband_roster.dart';
class WarbandDrawerWidget extends StatelessWidget { class WarbandDrawerWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
GitHubAdapter github = Provider.of<GitHubAdapter>(context); GitHubAdapter github = Provider.of<GitHubAdapter>(context);
WarbandRoaster activeRoaster = github.activeRoaster(); // No settings at all
if (github.repository == null) {
if (activeRoaster == null) {
// Add search button
return Padding( return Padding(
padding: const EdgeInsets.only(top: 100, left: 30, right: 30), padding: const EdgeInsets.only(top: 100, left: 30, right: 30),
child: Column(children: <Widget>[ child: Column(children: <Widget>[
@ -31,10 +31,68 @@ class WarbandDrawerWidget extends StatelessWidget {
); );
} }
List<WarbandRoaster> roasters = github.roasters; // Never fetched any data
if (github.activeRoster() == null) {
return Padding(
padding: const EdgeInsets.only(top: 100, left: 30, right: 30),
child: Column(children: <Widget>[
Text(
'The repository is set. You can now search for warbands. This can take a while.'),
FlatButton(
onPressed: () {
github.search();
},
child: Text(
'Search for warbands',
style: TextStyle(color: Colors.blue),
),
),
FlatButton(
onPressed: () {
Navigator.popAndPushNamed(context, '/settings');
},
child: Text(
'Change settings',
style: TextStyle(color: Colors.blue),
),
),
Visibility(
visible: github.isSyncInProgress,
child: CircularProgressIndicator(),
),
Visibility(
visible: github.syncErrors.length > 0,
child: buildSyncErrors(context),
)
]),
);
}
return buildRosterList(context);
}
Widget buildSyncErrors(BuildContext context) {
List<Widget> syncErrors = new List();
GitHubAdapter github = Provider.of<GitHubAdapter>(context);
// TODO: Make it pretty
github.syncErrors.forEach((error) {
syncErrors.add(Text(error, style: TextStyle(color: Colors.red)));
});
return Column(children: syncErrors);
}
Widget buildRosterList(BuildContext context) {
GitHubAdapter github = Provider.of<GitHubAdapter>(context);
WarbandRoster activeroster = github.activeRoster();
List<WarbandRoster> rosters = github.rosters;
List<Widget> tiles = new List(); List<Widget> tiles = new List();
tiles.add(Visibility(
visible: github.isSyncInProgress, child: LinearProgressIndicator()));
// Show some stats for the own warband // Show some stats for the own warband
tiles.add(UserAccountsDrawerHeader( tiles.add(UserAccountsDrawerHeader(
otherAccountsPictures: <Widget>[ otherAccountsPictures: <Widget>[
@ -60,33 +118,31 @@ class WarbandDrawerWidget extends StatelessWidget {
}, },
), ),
], ],
accountName: Text(activeRoaster.name), accountName: Text(activeroster.name),
accountEmail: Text(activeRoaster.race), accountEmail: Text(activeroster.race),
)); ));
// TODO: Order Players on CP or rating rosters.forEach((roster) {
roasters.forEach((roaster) {
// We mark inactive warbands with a gray acent // We mark inactive warbands with a gray acent
var textColor = Colors.black; var textColor = Colors.black;
if (!roaster.active) { if (!roster.active) {
textColor = Colors.black45; textColor = Colors.black45;
} }
tiles.add(ListTile( tiles.add(ListTile(
onTap: () { onTap: () {
github.changeActiveRoaster(roaster.playerName); github.changeActiveRoster(roster.playerName);
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
title: Text(roaster.name + ' (' + roaster.playerName + ')', title: Text(roster.name + ' (' + roster.playerName + ')',
style: TextStyle(color: textColor)), style: TextStyle(color: textColor)),
subtitle: Text(roaster.currentVersion.message), subtitle: Text(roster.currentVersion.message),
isThreeLine: true, isThreeLine: true,
trailing: Badge( trailing: Badge(
badgeColor: Colors.black12, badgeColor: Colors.black12,
shape: BadgeShape.square, shape: BadgeShape.square,
borderRadius: 2, borderRadius: 2,
badgeContent: Text(roaster.campaignPoints.toString() + ' CP', badgeContent: Text(roster.campaignPoints.toString() + ' CP',
style: TextStyle(color: Colors.white)), style: TextStyle(color: Colors.white)),
), ),
)); ));

View file

@ -1,5 +1,5 @@
# Generated by pub # Generated by pub
# See https://www.dartlang.org/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
analyzer: analyzer:
dependency: transitive dependency: transitive
@ -7,7 +7,7 @@ packages:
name: analyzer name: analyzer
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.36.3" version: "0.36.4"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -21,7 +21,7 @@ packages:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.2.0"
badges: badges:
dependency: "direct main" dependency: "direct main"
description: description:
@ -42,7 +42,7 @@ packages:
name: build name: build
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.4" version: "1.1.5"
build_config: build_config:
dependency: transitive dependency: transitive
description: description:
@ -63,14 +63,14 @@ packages:
name: build_resolvers name: build_resolvers
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.5" version: "1.0.6"
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.6.0" version: "1.6.1"
build_runner_core: build_runner_core:
dependency: transitive dependency: transitive
description: description:
@ -91,7 +91,7 @@ packages:
name: built_value name: built_value
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.6.0" version: "6.7.0"
charcode: charcode:
dependency: transitive dependency: transitive
description: description:
@ -140,7 +140,7 @@ packages:
name: csslib name: csslib
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.16.0" version: "0.16.1"
cupertino_icons: cupertino_icons:
dependency: "direct main" dependency: "direct main"
description: description:
@ -154,7 +154,7 @@ packages:
name: dart_style name: dart_style
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.7" version: "1.2.9"
fixnum: fixnum:
dependency: transitive dependency: transitive
description: description:
@ -178,7 +178,7 @@ packages:
name: front_end name: front_end
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.18" version: "0.1.19"
glob: glob:
dependency: transitive dependency: transitive
description: description:
@ -255,7 +255,7 @@ packages:
name: kernel name: kernel
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.18" version: "0.3.19"
logging: logging:
dependency: transitive dependency: transitive
description: description:
@ -311,7 +311,7 @@ packages:
name: pedantic name: pedantic
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.5.0" version: "1.7.0"
pool: pool:
dependency: transitive dependency: transitive
description: description:
@ -319,6 +319,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
preferences:
dependency: "direct main"
description:
name: preferences
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
provider: provider:
dependency: "direct main" dependency: "direct main"
description: description:
@ -346,7 +353,14 @@ packages:
name: quiver name: quiver
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.3"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.3+2"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:
@ -421,7 +435,7 @@ packages:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.4" version: "0.2.5"
timing: timing:
dependency: transitive dependency: transitive
description: description:
@ -449,14 +463,14 @@ packages:
name: watcher name: watcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.9.7+10" version: "0.9.7+12"
web_socket_channel: web_socket_channel:
dependency: transitive dependency: transitive
description: description:
name: web_socket_channel name: web_socket_channel
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.13" version: "1.0.14"
yaml: yaml:
dependency: "direct main" dependency: "direct main"
description: description:
@ -465,5 +479,5 @@ packages:
source: hosted source: hosted
version: "2.1.16" version: "2.1.16"
sdks: sdks:
dart: ">=2.3.0-dev.0.1 <3.0.0" dart: ">=2.3.0 <3.0.0"
flutter: ">=0.2.5 <2.0.0" flutter: ">=1.5.0 <2.0.0"

View file

@ -24,6 +24,8 @@ dependencies:
http: http:
provider: provider:
badges: badges:
shared_preferences:
preferences:
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.