mirror of
https://github.com/chenasraf/mudblock.git
synced 2026-05-18 01:48:57 +00:00
feat: aliases crud wip
This commit is contained in:
@@ -11,9 +11,9 @@ enum MUDActionTarget {
|
||||
}
|
||||
|
||||
class MUDAction {
|
||||
final String content;
|
||||
final MUDActionTarget sendTo;
|
||||
const MUDAction(this.content, {this.sendTo = MUDActionTarget.world});
|
||||
String content;
|
||||
MUDActionTarget sendTo;
|
||||
MUDAction(this.content, {this.sendTo = MUDActionTarget.world});
|
||||
|
||||
void invoke(BuildContext context, List<String> matches) {
|
||||
final store = Provider.of<GameStore>(context, listen: false);
|
||||
@@ -41,5 +41,17 @@ class MUDAction {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
factory MUDAction.empty() => MUDAction('');
|
||||
|
||||
factory MUDAction.fromJson(Map<String, dynamic> json) => MUDAction(
|
||||
json['content'],
|
||||
sendTo: MUDActionTarget.values[json['sendTo']],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'content': content,
|
||||
'sendTo': sendTo.index,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import '../string_utils.dart';
|
||||
import 'action.dart';
|
||||
|
||||
class Alias {
|
||||
@@ -10,7 +11,7 @@ class Alias {
|
||||
bool isCaseSensitive;
|
||||
bool isRemovedFromBuffer;
|
||||
bool isTemporary;
|
||||
int invokeCount = 0;
|
||||
int invokeCount;
|
||||
MUDAction action;
|
||||
|
||||
Alias({
|
||||
@@ -22,8 +23,45 @@ class Alias {
|
||||
this.isCaseSensitive = false,
|
||||
this.isRemovedFromBuffer = false,
|
||||
this.isTemporary = false,
|
||||
this.invokeCount = 0,
|
||||
});
|
||||
|
||||
factory Alias.empty() => Alias(
|
||||
id: uuid(),
|
||||
pattern: '',
|
||||
enabled: true,
|
||||
isRegex: false,
|
||||
isCaseSensitive: false,
|
||||
isRemovedFromBuffer: false,
|
||||
isTemporary: false,
|
||||
invokeCount: 0,
|
||||
action: MUDAction.empty(),
|
||||
);
|
||||
|
||||
factory Alias.fromJson(Map<String, dynamic> json) => Alias(
|
||||
id: json['id'],
|
||||
pattern: json['pattern'],
|
||||
enabled: json['enabled'],
|
||||
isRegex: json['isRegex'],
|
||||
isCaseSensitive: json['isCaseSensitive'],
|
||||
isRemovedFromBuffer: json['isRemovedFromBuffer'],
|
||||
isTemporary: json['isTemporary'],
|
||||
invokeCount: json['invokeCount'],
|
||||
action: MUDAction.fromJson(json['action']),
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'id': id,
|
||||
'pattern': pattern,
|
||||
'enabled': enabled,
|
||||
'isRegex': isRegex,
|
||||
'isCaseSensitive': isCaseSensitive,
|
||||
'isRemovedFromBuffer': isRemovedFromBuffer,
|
||||
'isTemporary': isTemporary,
|
||||
'invokeCount': invokeCount,
|
||||
'action': action.toJson(),
|
||||
};
|
||||
|
||||
bool matches(String line) {
|
||||
if (isRegex) {
|
||||
final regex = RegExp(pattern, caseSensitive: isCaseSensitive);
|
||||
@@ -46,7 +84,8 @@ class Alias {
|
||||
return [];
|
||||
}
|
||||
if (isRegex) {
|
||||
final regex = RegExp(pattern, caseSensitive: isCaseSensitive, unicode: true);
|
||||
final regex =
|
||||
RegExp(pattern, caseSensitive: isCaseSensitive, unicode: true);
|
||||
final rMatches = regex.allMatches(str);
|
||||
final matches = <String>[];
|
||||
for (var i = 0; i < rMatches.length; i++) {
|
||||
@@ -72,5 +111,28 @@ class Alias {
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
|
||||
Alias copyWith({
|
||||
String? id,
|
||||
String? pattern,
|
||||
bool? enabled,
|
||||
bool? isRegex,
|
||||
bool? isCaseSensitive,
|
||||
bool? isRemovedFromBuffer,
|
||||
bool? isTemporary,
|
||||
int? invokeCount = 0,
|
||||
MUDAction? action,
|
||||
}) =>
|
||||
Alias(
|
||||
id: id ?? this.id,
|
||||
pattern: pattern ?? this.pattern,
|
||||
enabled: enabled ?? this.enabled,
|
||||
isRegex: isRegex ?? this.isRegex,
|
||||
isCaseSensitive: isCaseSensitive ?? this.isCaseSensitive,
|
||||
isRemovedFromBuffer: isRemovedFromBuffer ?? this.isRemovedFromBuffer,
|
||||
isTemporary: isTemporary ?? this.isTemporary,
|
||||
invokeCount: invokeCount ?? this.invokeCount,
|
||||
action: action ?? this.action,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mudblock/core/storage.dart';
|
||||
|
||||
import 'alias.dart';
|
||||
import 'trigger.dart';
|
||||
|
||||
class MUDProfile {
|
||||
String id;
|
||||
String name;
|
||||
@@ -10,5 +16,27 @@ class MUDProfile {
|
||||
required this.host,
|
||||
required this.port,
|
||||
});
|
||||
|
||||
Future<List<Trigger>> loadTriggers() async {
|
||||
debugPrint('MUDProfile.loadTriggers: $id');
|
||||
final triggers = await ProfileStorage.listProfileFiles(id, 'triggers');
|
||||
return triggers.values.map((e) => Trigger.fromJson(e)).toList();
|
||||
}
|
||||
|
||||
Future<List<Alias>> loadAliases() async {
|
||||
debugPrint('MUDProfile.loadAliases: $id');
|
||||
final aliases = await ProfileStorage.listProfileFiles(id, 'aliases');
|
||||
return aliases.values.map((e) => Alias.fromJson(e)).toList();
|
||||
}
|
||||
|
||||
Future<void> saveAlias(Alias alias) async {
|
||||
debugPrint('MUDProfile.saveAlias: $id/aliases/${alias.id}');
|
||||
return ProfileStorage.writeProfileFile(id, 'aliases/${alias.id}', alias.toJson());
|
||||
}
|
||||
|
||||
Future<void> saveTrigger(Trigger trigger) async {
|
||||
debugPrint('MUDProfile.saveTrigger: $id/triggers/${trigger.id}');
|
||||
return ProfileStorage.writeProfileFile(id, 'triggers/${trigger.id}', trigger.toJson());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'action.dart';
|
||||
import 'alias.dart';
|
||||
|
||||
class Trigger extends Alias {
|
||||
@@ -12,6 +11,43 @@ class Trigger extends Alias {
|
||||
super.isCaseSensitive = false,
|
||||
super.isRemovedFromBuffer = false,
|
||||
super.isTemporary = false,
|
||||
super.invokeCount = 0,
|
||||
});
|
||||
|
||||
factory Trigger.fromJson(Map<String, dynamic> json) => Trigger(
|
||||
id: json['id'],
|
||||
pattern: json['pattern'],
|
||||
enabled: json['enabled'],
|
||||
isRegex: json['isRegex'],
|
||||
isCaseSensitive: json['isCaseSensitive'],
|
||||
isRemovedFromBuffer: json['isRemovedFromBuffer'],
|
||||
isTemporary: json['isTemporary'],
|
||||
invokeCount: json['invokeCount'],
|
||||
action: MUDAction.fromJson(json['action']),
|
||||
);
|
||||
|
||||
@override
|
||||
Trigger copyWith({
|
||||
String? id,
|
||||
String? pattern,
|
||||
bool? enabled,
|
||||
bool? isRegex,
|
||||
bool? isCaseSensitive,
|
||||
bool? isRemovedFromBuffer,
|
||||
bool? isTemporary,
|
||||
int? invokeCount = 0,
|
||||
MUDAction? action,
|
||||
}) =>
|
||||
Trigger(
|
||||
id: id ?? this.id,
|
||||
pattern: pattern ?? this.pattern,
|
||||
enabled: enabled ?? this.enabled,
|
||||
isRegex: isRegex ?? this.isRegex,
|
||||
isCaseSensitive: isCaseSensitive ?? this.isCaseSensitive,
|
||||
isRemovedFromBuffer: isRemovedFromBuffer ?? this.isRemovedFromBuffer,
|
||||
isTemporary: isTemporary ?? this.isTemporary,
|
||||
invokeCount: invokeCount ?? this.invokeCount,
|
||||
action: action ?? this.action,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
80
lib/core/storage.dart
Normal file
80
lib/core/storage.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:localstore/localstore.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
class FileStorage {
|
||||
static final Localstore _store = Localstore.instance;
|
||||
|
||||
static Future<Map<String, dynamic>?> readFile(String filename) async {
|
||||
debugPrint('Getting file: $filename');
|
||||
final collection = path.dirname(filename);
|
||||
filename = path.basename(filename);
|
||||
return _store.collection(collection).doc(filename).get();
|
||||
}
|
||||
|
||||
static Future<void> writeFile(
|
||||
String filename, Map<String, dynamic> data) async {
|
||||
debugPrint(
|
||||
'Setting file: $filename, data: ${data.toString().length} bytes');
|
||||
final collection = path.dirname(filename);
|
||||
filename = path.basename(filename);
|
||||
await _store.collection(collection).doc(filename).set(data);
|
||||
}
|
||||
|
||||
static Future<void> deleteFile(String filename) async {
|
||||
debugPrint('Deleting file: $filename');
|
||||
final collection = path.dirname(filename);
|
||||
filename = path.basename(filename);
|
||||
await _store.collection(collection).doc(filename).delete();
|
||||
}
|
||||
|
||||
static Future<Map<String, Map<String, dynamic>>> readDirectory(
|
||||
String collection,
|
||||
) async {
|
||||
final docs = await _store.collection(collection).get();
|
||||
debugPrint('Listing collection: $collection, ${docs?.length} docs');
|
||||
return (docs ?? {}).cast<String, Map<String, dynamic>>();
|
||||
}
|
||||
|
||||
static Future<void> deleteDirectory(String collection) async {
|
||||
debugPrint('Clearing collection: $collection');
|
||||
await _store.collection(collection).delete();
|
||||
}
|
||||
}
|
||||
|
||||
class ProfileStorage {
|
||||
static Future<Map<String, dynamic>?> readProfileFile(
|
||||
String profile, String filename) async {
|
||||
return FileStorage.readFile('profiles/$profile/$filename');
|
||||
}
|
||||
|
||||
static Future<void> writeProfileFile(
|
||||
String profile, String filename, Map<String, dynamic> data) async {
|
||||
await FileStorage.writeFile('profiles/$profile/$filename', data);
|
||||
}
|
||||
|
||||
static Future<void> deleteProfile(String profile) async {
|
||||
await FileStorage.deleteDirectory('profiles/$profile');
|
||||
}
|
||||
|
||||
static Future<void> deleteProfileFile(String profile, String filename) async {
|
||||
await FileStorage.deleteFile('profiles/$profile/$filename');
|
||||
}
|
||||
|
||||
static Future<Map<String, Map<String, dynamic>>> listAllProfiles() async {
|
||||
return FileStorage.readDirectory('profiles');
|
||||
}
|
||||
|
||||
static Future<Map<String, Map<String, dynamic>>> listProfileFiles(
|
||||
String profile, [
|
||||
String? directory,
|
||||
]) async {
|
||||
return FileStorage.readDirectory(
|
||||
'profiles/$profile${directory != null ? '/$directory' : ''}');
|
||||
}
|
||||
|
||||
static Future<void> deleteAllProfiles() async {
|
||||
await FileStorage.deleteDirectory('profiles');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
@@ -40,8 +39,6 @@ class GameStore extends ChangeNotifier {
|
||||
GameStore init() {
|
||||
debugPrint('GameStore.init');
|
||||
scrollController = ScrollController();
|
||||
loadTriggers();
|
||||
loadAliases();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -61,51 +58,25 @@ class GameStore extends ChangeNotifier {
|
||||
onData: onData,
|
||||
onError: onError,
|
||||
);
|
||||
await Future.wait([
|
||||
loadTriggers(),
|
||||
loadAliases(),
|
||||
]);
|
||||
_client.connect();
|
||||
}
|
||||
|
||||
void loadTriggers() {
|
||||
Future<void> loadTriggers() async {
|
||||
debugPrint('loadTriggers');
|
||||
final list = await currentProfile.loadTriggers();
|
||||
triggers.clear();
|
||||
triggers.addAll(
|
||||
[
|
||||
Trigger(
|
||||
id: 'test',
|
||||
pattern: r'^You are in the ([^.]+)\. This is the ([^.]+)\.',
|
||||
action: const MUDAction(
|
||||
'Hello, %1, the %2!',
|
||||
sendTo: MUDActionTarget.world,
|
||||
),
|
||||
isRegex: true,
|
||||
),
|
||||
Trigger(
|
||||
id: 'test2',
|
||||
pattern: r'^exits: ([\w\s]+)',
|
||||
action: const MUDAction(
|
||||
'I see exits: %1',
|
||||
sendTo: MUDActionTarget.world,
|
||||
),
|
||||
isRegex: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
triggers.addAll(list);
|
||||
debugPrint('triggers: ${triggers.length}');
|
||||
}
|
||||
|
||||
void loadAliases() {
|
||||
Future<void> loadAliases() async {
|
||||
final list = await currentProfile.loadAliases();
|
||||
aliases.clear();
|
||||
aliases.addAll(
|
||||
[
|
||||
Alias(
|
||||
id: 'hello',
|
||||
pattern: r'^hello|^hi',
|
||||
action: const MUDAction(
|
||||
'Hello, world!',
|
||||
sendTo: MUDActionTarget.world,
|
||||
),
|
||||
isRegex: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
aliases.addAll(list);
|
||||
debugPrint('aliases: ${aliases.length}');
|
||||
}
|
||||
|
||||
@@ -300,9 +271,30 @@ class GameStore extends ChangeNotifier {
|
||||
input.text = content;
|
||||
selectInput();
|
||||
}
|
||||
|
||||
static consumer(
|
||||
Widget Function(BuildContext context, GameStore value, Widget? child) builder,
|
||||
) {
|
||||
return Consumer<GameStore>(
|
||||
builder: builder,
|
||||
);
|
||||
}
|
||||
|
||||
static provider({
|
||||
required Widget child,
|
||||
}) {
|
||||
return ChangeNotifierProvider<GameStore>.value(
|
||||
value: gameStore,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mixin GameStoreMixin<T extends StatefulWidget> on State<T> {
|
||||
mixin GameStoreMixin {
|
||||
GameStore storeOf(BuildContext context) => Provider.of<GameStore>(context, listen: false);
|
||||
}
|
||||
|
||||
mixin GameStoreStateMixin<T extends StatefulWidget> on State<T> {
|
||||
GameStore get store => Provider.of<GameStore>(context, listen: false);
|
||||
}
|
||||
|
||||
|
||||
8
lib/core/string_utils.dart
Normal file
8
lib/core/string_utils.dart
Normal file
@@ -0,0 +1,8 @@
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
const _uuid = Uuid();
|
||||
|
||||
String uuid() {
|
||||
return _uuid.v4();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mudblock/core/consts.dart';
|
||||
import 'package:mudblock/core/store.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import 'core/features/alias.dart';
|
||||
import 'core/storage/shared_prefs.dart';
|
||||
import 'core/store.dart';
|
||||
import 'pages/alias_page.dart';
|
||||
import 'pages/alias_list_page.dart';
|
||||
import 'pages/home_page.dart';
|
||||
import 'pages/main_scaffold.dart';
|
||||
import 'pages/profile_select_page.dart';
|
||||
@@ -43,14 +45,26 @@ class MyApp extends StatelessWidget {
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
||||
useMaterial3: true,
|
||||
),
|
||||
builder: (context, child) {
|
||||
return GameStore.provider(child: child!);
|
||||
},
|
||||
initialRoute: '/home',
|
||||
routes: {
|
||||
'/select-profile': (context) => const ProfileSelectPage(),
|
||||
'/aliases': (context) => GameStore.consumer(
|
||||
(context, store, child) {
|
||||
return const AliasListPage();
|
||||
},
|
||||
),
|
||||
'/alias': (context) {
|
||||
final alias = ModalRoute.of(context)!.settings.arguments as Alias?;
|
||||
return AliasPage(alias: alias);
|
||||
},
|
||||
'/home': (context) => MainScaffold(
|
||||
builder: (context, _) {
|
||||
return HomePage(key: homeKey);
|
||||
}
|
||||
),
|
||||
builder: (context, _) {
|
||||
return HomePage(key: homeKey);
|
||||
},
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
45
lib/pages/alias_list_page.dart
Normal file
45
lib/pages/alias_list_page.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:mudblock/core/store.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../core/features/alias.dart';
|
||||
|
||||
class AliasListPage extends StatelessWidget with GameStoreMixin {
|
||||
const AliasListPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var store = storeOf(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Aliases'),
|
||||
),
|
||||
body: Consumer<GameStore>(
|
||||
builder: (context, store, child) {
|
||||
final aliases = store.aliases;
|
||||
return ListView.builder(
|
||||
itemCount: aliases.length,
|
||||
itemBuilder: (context, item) => ListTile(
|
||||
title: Text(aliases[item].pattern),
|
||||
subtitle: Text(aliases[item].action.content),
|
||||
onTap: () {
|
||||
Navigator.pushNamed(context, '/alias',
|
||||
arguments: aliases[item]);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () async {
|
||||
final alias = await Navigator.pushNamed(context, '/alias');
|
||||
if (alias != null) {
|
||||
store.currentProfile.saveAlias(alias as Alias);
|
||||
}
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
82
lib/pages/alias_page.dart
Normal file
82
lib/pages/alias_page.dart
Normal file
@@ -0,0 +1,82 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../core/features/alias.dart';
|
||||
|
||||
class AliasPage extends StatefulWidget {
|
||||
const AliasPage({super.key, required this.alias});
|
||||
|
||||
final Alias? alias;
|
||||
|
||||
@override
|
||||
State<AliasPage> createState() => _AliasPageState();
|
||||
}
|
||||
|
||||
class _AliasPageState extends State<AliasPage> {
|
||||
late final Alias alias;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
alias = widget.alias?.copyWith() ?? Alias.empty();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Alias'),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: TextEditingController(text: alias.pattern),
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Pattern',
|
||||
),
|
||||
onChanged: (value) {
|
||||
alias.pattern = value;
|
||||
},
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: 300,
|
||||
child: CheckboxListTile(
|
||||
title: const Text('Regular Expression'),
|
||||
value: alias.isRegex,
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
onChanged: (value) {
|
||||
alias.isRegex = value ?? false;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
TextField(
|
||||
controller: TextEditingController(text: alias.action.content),
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Action',
|
||||
),
|
||||
onChanged: (value) {
|
||||
alias.action.content = value;
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context, alias);
|
||||
},
|
||||
child: const Icon(Icons.save),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ class HomePage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class HomePageState extends State<HomePage>
|
||||
with GameStoreMixin, WindowListener {
|
||||
with GameStoreStateMixin, WindowListener {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -128,8 +128,8 @@ class HomePageState extends State<HomePage>
|
||||
IconButton(
|
||||
icon: const Icon(Icons.bug_report),
|
||||
onPressed: () {
|
||||
store.loadTriggers();
|
||||
store.loadAliases();
|
||||
Navigator.pushNamed(context, '/aliases');
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
34
pubspec.lock
34
pubspec.lock
@@ -65,6 +65,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
ctelnet:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -184,7 +192,7 @@ packages:
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path
|
||||
sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
|
||||
@@ -340,6 +348,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
sprintf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sprintf
|
||||
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -380,6 +396,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
uuid:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: uuid
|
||||
sha256: b715b8d3858b6fa9f68f87d20d98830283628014750c2b09b6f516c1da4af2a7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -41,6 +41,8 @@ dependencies:
|
||||
shared_preferences: ^2.2.1
|
||||
easy_debounce: ^2.0.3
|
||||
localstore: ^1.3.5
|
||||
path: ^1.8.3
|
||||
uuid: ^4.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user