Implement food entry lookup on entering a food name.
Now, an on-the-fly food lookup is created from existing entries on startup. Those entries are used to make suggestions when the user is typing to enter new food entries.
This commit is contained in:
parent
fb0dbef158
commit
b83f547f6b
@ -1,3 +1,6 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:calodiary/storage/storage.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:calodiary/food_entry_bloc.dart';
|
import 'package:calodiary/food_entry_bloc.dart';
|
||||||
import 'package:calodiary/row_with_spacers_widget.dart';
|
import 'package:calodiary/row_with_spacers_widget.dart';
|
||||||
@ -16,22 +19,53 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
|
|||||||
TextEditingController nameController = TextEditingController();
|
TextEditingController nameController = TextEditingController();
|
||||||
TextEditingController massController = TextEditingController();
|
TextEditingController massController = TextEditingController();
|
||||||
TextEditingController kcalPerMassController = TextEditingController();
|
TextEditingController kcalPerMassController = TextEditingController();
|
||||||
|
Map<String, double> suggestions = {};
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
suggestions = FoodStorage.getInstance().getFoodEntryLookupDatabase;
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var nameWidget = TextField(
|
var nameWidget = Autocomplete<String>(
|
||||||
decoration: const InputDecoration(hintText: "Name"),
|
optionsViewOpenDirection: OptionsViewOpenDirection.down,
|
||||||
controller: nameController,
|
fieldViewBuilder: (context, controller, focusNode, onSubmitted) {
|
||||||
);
|
nameController = controller;
|
||||||
|
return TextFormField(
|
||||||
|
controller: controller,
|
||||||
|
focusNode: focusNode,
|
||||||
|
decoration: const InputDecoration(label: Text("Name")));
|
||||||
|
},
|
||||||
|
optionsBuilder: (TextEditingValue textEditingValue) {
|
||||||
|
if (textEditingValue.text == '') {
|
||||||
|
return const Iterable<String>.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return suggestions.keys.where(
|
||||||
|
(name) {
|
||||||
|
return name
|
||||||
|
.toLowerCase()
|
||||||
|
.contains(textEditingValue.text.toLowerCase());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onSelected: (selectedFood) {
|
||||||
|
double kcalPerMassForSelectedFood = suggestions[selectedFood]!;
|
||||||
|
setState(() {
|
||||||
|
kcalPerMassController.text = kcalPerMassForSelectedFood.toString();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
var massWidget = TextField(
|
var massWidget = TextField(
|
||||||
decoration: const InputDecoration(hintText: "Menge"),
|
decoration: const InputDecoration(label: Text("Menge")),
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
controller: massController,
|
controller: massController,
|
||||||
);
|
);
|
||||||
|
|
||||||
var kcalPerMassWidget = TextField(
|
var kcalPerMassWidget = TextField(
|
||||||
decoration: const InputDecoration(hintText: "kcal pro 100g"),
|
decoration: const InputDecoration(label: Text("kcal pro")),
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
controller: kcalPerMassController);
|
controller: kcalPerMassController);
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import 'package:uuid/uuid.dart';
|
|||||||
|
|
||||||
class FoodEntryBloc extends Bloc<FoodEvent, FoodEntryState> {
|
class FoodEntryBloc extends Bloc<FoodEvent, FoodEntryState> {
|
||||||
final FoodEntryState initialState;
|
final FoodEntryState initialState;
|
||||||
final AppStorage storage;
|
final FoodStorage storage;
|
||||||
final DateTime forDate;
|
final DateTime forDate;
|
||||||
|
|
||||||
FoodEntryBloc(
|
FoodEntryBloc(
|
||||||
@ -12,16 +12,18 @@ class FoodEntryBloc extends Bloc<FoodEvent, FoodEntryState> {
|
|||||||
required this.forDate,
|
required this.forDate,
|
||||||
required this.storage})
|
required this.storage})
|
||||||
: super(initialState) {
|
: super(initialState) {
|
||||||
on<FoodEntryEvent>(addFoodEntry);
|
on<FoodEntryEvent>(handleFoodEntryEvent);
|
||||||
on<FoodDeletionEvent>(deleteFood);
|
on<FoodDeletionEvent>(deleteFood);
|
||||||
on<PageChangedEvent>(updateEntries);
|
on<PageChangedEvent>(updateEntries);
|
||||||
}
|
}
|
||||||
|
|
||||||
void addFoodEntry(FoodEntryEvent event, Emitter<FoodEntryState> emit) async {
|
void handleFoodEntryEvent(
|
||||||
|
FoodEntryEvent event, Emitter<FoodEntryState> emit) async {
|
||||||
FoodEntryState newState = FoodEntryState.from(state);
|
FoodEntryState newState = FoodEntryState.from(state);
|
||||||
newState.addEntry(event.entry);
|
newState.addEntry(event.entry);
|
||||||
|
|
||||||
await storage.writeEntriesForDate(forDate, newState.foodEntries);
|
await storage.writeEntriesForDate(forDate, newState.foodEntries);
|
||||||
|
storage.addFoodEntryToLookupDatabase(event.entry);
|
||||||
|
|
||||||
emit(newState);
|
emit(newState);
|
||||||
}
|
}
|
||||||
|
@ -4,27 +4,34 @@ import 'package:calodiary/row_with_spacers_widget.dart';
|
|||||||
|
|
||||||
class FoodEntryWidget extends StatelessWidget {
|
class FoodEntryWidget extends StatelessWidget {
|
||||||
final FoodEntry entry;
|
final FoodEntry entry;
|
||||||
final Function(BuildContext context) onDelete;
|
final Function(BuildContext context, String id) onDelete;
|
||||||
|
|
||||||
const FoodEntryWidget(
|
const FoodEntryWidget(
|
||||||
{super.key, required this.entry, required this.onDelete});
|
{super.key, required this.entry, required this.onDelete});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Card(
|
return Dismissible(
|
||||||
child: Padding(
|
key: ValueKey(entry.id),
|
||||||
padding: const EdgeInsets.only(left: 4.0),
|
onDismissed: (direction) {
|
||||||
child: RowWidget(
|
onDelete(context, entry.id);
|
||||||
Text(entry.name),
|
},
|
||||||
Text(entry.mass.ceil().toString()),
|
child: Card(
|
||||||
Text(entry.kcalPerMass.ceil().toString()),
|
elevation: 5.0,
|
||||||
Text((entry.mass * entry.kcalPerMass / 100).ceil().toString()),
|
child: Padding(
|
||||||
IconButton(
|
padding: const EdgeInsets.only(left: 4.0),
|
||||||
style: IconButton.styleFrom(padding: EdgeInsets.zero),
|
child: RowWidget(
|
||||||
onPressed: () {
|
Text(entry.name),
|
||||||
onDelete(context);
|
Text(entry.mass.ceil().toString()),
|
||||||
},
|
Text(entry.kcalPerMass.ceil().toString()),
|
||||||
icon: const Icon(Icons.delete_forever_rounded)),
|
Text((entry.mass * entry.kcalPerMass / 100).ceil().toString()),
|
||||||
|
IconButton(
|
||||||
|
style: IconButton.styleFrom(padding: EdgeInsets.zero),
|
||||||
|
onPressed: () {
|
||||||
|
onDelete(context, entry.id);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.delete_forever_rounded)),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -10,7 +10,8 @@ import 'package:go_router/go_router.dart';
|
|||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
var storage = await AppStorage.create();
|
var storage = await FoodStorage.create();
|
||||||
|
await storage.buildFoodLookupDatabase();
|
||||||
var kcalLimit = await storage.readLimit();
|
var kcalLimit = await storage.readLimit();
|
||||||
var brightness = await storage.readBrightness();
|
var brightness = await storage.readBrightness();
|
||||||
|
|
||||||
@ -24,7 +25,7 @@ void main() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MainApp extends StatelessWidget {
|
class MainApp extends StatelessWidget {
|
||||||
final AppStorage storage;
|
final FoodStorage storage;
|
||||||
final double kcalLimit;
|
final double kcalLimit;
|
||||||
final String brightness;
|
final String brightness;
|
||||||
|
|
||||||
@ -36,38 +37,41 @@ class MainApp extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiBlocProvider(
|
return SafeArea(
|
||||||
providers: [
|
child: MultiBlocProvider(
|
||||||
BlocProvider(
|
providers: [
|
||||||
create: (context) => SettingsDataBloc(
|
BlocProvider(
|
||||||
SettingsState(kcalLimit: kcalLimit),
|
create: (context) => SettingsDataBloc(
|
||||||
storage: storage),
|
SettingsState(kcalLimit: kcalLimit),
|
||||||
),
|
storage: storage),
|
||||||
BlocProvider(
|
),
|
||||||
create: (context) => ThemeDataBloc(ThemeState(brightness: brightness),
|
BlocProvider(
|
||||||
storage: storage),
|
create: (context) => ThemeDataBloc(
|
||||||
),
|
ThemeState(brightness: brightness),
|
||||||
],
|
storage: storage),
|
||||||
child: BlocBuilder<ThemeDataBloc, ThemeState>(
|
),
|
||||||
builder: (context, state) {
|
],
|
||||||
var switchToTheme = ThemeData.light();
|
child: BlocBuilder<ThemeDataBloc, ThemeState>(
|
||||||
if (state.brightness == 'dark') {
|
builder: (context, state) {
|
||||||
switchToTheme = ThemeData.dark();
|
var switchToTheme = ThemeData.light();
|
||||||
}
|
if (state.brightness == 'dark') {
|
||||||
|
switchToTheme = ThemeData.dark();
|
||||||
|
}
|
||||||
|
|
||||||
return MaterialApp.router(
|
return MaterialApp.router(
|
||||||
localizationsDelegates: const [
|
localizationsDelegates: const [
|
||||||
GlobalMaterialLocalizations.delegate,
|
GlobalMaterialLocalizations.delegate,
|
||||||
GlobalWidgetsLocalizations.delegate,
|
GlobalWidgetsLocalizations.delegate,
|
||||||
GlobalCupertinoLocalizations.delegate,
|
GlobalCupertinoLocalizations.delegate,
|
||||||
],
|
],
|
||||||
supportedLocales: const [
|
supportedLocales: const [
|
||||||
Locale('de'),
|
Locale('de'),
|
||||||
],
|
],
|
||||||
theme: switchToTheme,
|
theme: switchToTheme,
|
||||||
routerConfig: router,
|
routerConfig: router,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ class PerDateWidget extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _PerDateWidgetState extends State<PerDateWidget> {
|
class _PerDateWidgetState extends State<PerDateWidget> {
|
||||||
late AppStorage storage;
|
late FoodStorage storage;
|
||||||
late Future<List<FoodEntry>> entriesFuture;
|
late Future<List<FoodEntry>> entriesFuture;
|
||||||
late List<FoodEntry> entries;
|
late List<FoodEntry> entries;
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ class _PerDateWidgetState extends State<PerDateWidget> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
storage = AppStorage.getInstance();
|
storage = FoodStorage.getInstance();
|
||||||
entriesFuture = storage.getEntriesForDate(widget.date);
|
entriesFuture = storage.getEntriesForDate(widget.date);
|
||||||
entriesFuture.then((val) {
|
entriesFuture.then((val) {
|
||||||
entries = val;
|
entries = val;
|
||||||
@ -93,15 +93,20 @@ class _PerDateWidgetState extends State<PerDateWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return FoodEntryWidget(
|
return Column(
|
||||||
entry: state.foodEntries[index],
|
children: [
|
||||||
onDelete: (callbackContext) {
|
FoodEntryWidget(
|
||||||
callbackContext
|
entry: state.foodEntries[index],
|
||||||
.read<FoodEntryBloc>()
|
onDelete: (callbackContext, id) {
|
||||||
.add(FoodDeletionEvent(
|
callbackContext
|
||||||
entryID: state.foodEntries[index].id,
|
.read<FoodEntryBloc>()
|
||||||
));
|
.add(FoodDeletionEvent(
|
||||||
},
|
entryID: id,
|
||||||
|
));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -2,7 +2,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
import 'package:calodiary/storage/storage.dart';
|
import 'package:calodiary/storage/storage.dart';
|
||||||
|
|
||||||
class SettingsDataBloc extends Bloc<SettingsEvent, SettingsState> {
|
class SettingsDataBloc extends Bloc<SettingsEvent, SettingsState> {
|
||||||
final AppStorage storage;
|
final FoodStorage storage;
|
||||||
|
|
||||||
SettingsDataBloc(super.initialState, {required this.storage}) {
|
SettingsDataBloc(super.initialState, {required this.storage}) {
|
||||||
on<DailyKcalLimitUpdated>(persistDailyLimit);
|
on<DailyKcalLimitUpdated>(persistDailyLimit);
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
|
import 'dart:developer';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:calodiary/food_entry_bloc.dart';
|
import 'package:calodiary/food_entry_bloc.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:universal_platform/universal_platform.dart';
|
import 'package:universal_platform/universal_platform.dart';
|
||||||
|
|
||||||
class AppStorage {
|
class FoodStorage {
|
||||||
static late AppStorage _instance;
|
static late FoodStorage _instance;
|
||||||
late String path;
|
late String path;
|
||||||
AppStorage._create();
|
late Map<String, double> _foodLookupDatabase = {};
|
||||||
|
|
||||||
static Future<AppStorage> create() async {
|
FoodStorage._create();
|
||||||
var storage = AppStorage._create();
|
|
||||||
|
static Future<FoodStorage> create() async {
|
||||||
|
var storage = FoodStorage._create();
|
||||||
|
|
||||||
Directory dir = Directory('');
|
Directory dir = Directory('');
|
||||||
|
|
||||||
@ -26,7 +29,7 @@ class AppStorage {
|
|||||||
return _instance;
|
return _instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
static AppStorage getInstance() => _instance;
|
static FoodStorage getInstance() => _instance;
|
||||||
|
|
||||||
Future<List<FoodEntry>> getEntriesForDate(DateTime date) async {
|
Future<List<FoodEntry>> getEntriesForDate(DateTime date) async {
|
||||||
List<FoodEntry> entries = [];
|
List<FoodEntry> entries = [];
|
||||||
@ -132,4 +135,32 @@ class AppStorage {
|
|||||||
|
|
||||||
await file.writeAsString(brightness);
|
await file.writeAsString(brightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> buildFoodLookupDatabase() async {
|
||||||
|
// get a list of dates of the last 365 days
|
||||||
|
var dates = List<DateTime>.generate(365, (idx) {
|
||||||
|
var pastDay = Duration(days: idx);
|
||||||
|
return DateTime.now().subtract(pastDay);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var date in dates) {
|
||||||
|
addFoodEntryToLookupDatabaseFor(date);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> addFoodEntryToLookupDatabaseFor(DateTime date) async {
|
||||||
|
var entriesForDate = await getEntriesForDate(date);
|
||||||
|
|
||||||
|
for (var entry in entriesForDate) {
|
||||||
|
_foodLookupDatabase[entry.name] = entry.kcalPerMass;
|
||||||
|
log("Added entry: ${entry.name}/${entry.kcalPerMass}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addFoodEntryToLookupDatabase(FoodEntry entry) {
|
||||||
|
_foodLookupDatabase[entry.name] = entry.kcalPerMass;
|
||||||
|
log("Added entry: ${entry.name}/${entry.kcalPerMass}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, double> get getFoodEntryLookupDatabase => _foodLookupDatabase;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import 'package:calodiary/storage/storage.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class ThemeDataBloc extends Bloc<ThemeToggleEvent, ThemeState> {
|
class ThemeDataBloc extends Bloc<ThemeToggleEvent, ThemeState> {
|
||||||
final AppStorage storage;
|
final FoodStorage storage;
|
||||||
|
|
||||||
ThemeDataBloc(super.initialState, {required this.storage}) {
|
ThemeDataBloc(super.initialState, {required this.storage}) {
|
||||||
on<ThemeToggleEvent>(switchTheme);
|
on<ThemeToggleEvent>(switchTheme);
|
||||||
|
48
pubspec.lock
48
pubspec.lock
@ -53,10 +53,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: crypto
|
name: crypto
|
||||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.5"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -69,10 +69,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: ffi
|
name: ffi
|
||||||
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
|
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.3"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -121,10 +121,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: go_router
|
name: go_router
|
||||||
sha256: cdae1b9c8bd7efadcef6112e81c903662ef2ce105cbd220a04bbb7c3425b5554
|
sha256: "2ddb88e9ad56ae15ee144ed10e33886777eb5ca2509a914850a5faa7b52ff459"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.2.0"
|
version: "14.2.7"
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -193,10 +193,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: "25dfcaf170a0190f47ca6355bdd4552cb8924b430512ff0cafb8db9bd41fe33b"
|
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.14.0"
|
version: "1.15.0"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -217,18 +217,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: path_provider
|
name: path_provider
|
||||||
sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
|
sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.4"
|
||||||
path_provider_android:
|
path_provider_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_android
|
name: path_provider_android
|
||||||
sha256: "9c96da072b421e98183f9ea7464898428e764bc0ce5567f27ec8693442e72514"
|
sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.5"
|
version: "2.2.10"
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -257,10 +257,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_windows
|
name: path_provider_windows
|
||||||
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.1"
|
version: "2.3.0"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -350,10 +350,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "2419f20b0c8677b2d67c8ac4d1ac7372d862dc6c460cdbb052b40155408cd794"
|
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.1"
|
version: "0.7.2"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -374,10 +374,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: uuid
|
name: uuid
|
||||||
sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8"
|
sha256: f33d6bb662f0e4f79dcd7ada2e6170f3b3a2530c28fc41f49a411ddedd576a77
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.4.0"
|
version: "4.5.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -390,18 +390,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: "7475cb4dd713d57b6f7464c0e13f06da0d535d8b2067e188962a59bac2cf280b"
|
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.2.2"
|
version: "14.2.5"
|
||||||
win32:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: win32
|
|
||||||
sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "5.5.1"
|
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
Loading…
Reference in New Issue
Block a user