mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2024-12-25 15:02:33 +01:00
feat: Better encryption / verification
This commit is contained in:
parent
ede963fffc
commit
1ff986e7be
@ -1,3 +1,11 @@
|
|||||||
|
# Version 0.22.0
|
||||||
|
### Features
|
||||||
|
- Broadcast self-verification
|
||||||
|
### Changes
|
||||||
|
- Undecryptable events have a "verify" button, if you haven't verified yet
|
||||||
|
- User bottom sheet lists verified status
|
||||||
|
- Lock icon next to input bar can now be red
|
||||||
|
|
||||||
# Version 0.21.0 - 2020-10-28
|
# Version 0.21.0 - 2020-10-28
|
||||||
### Features
|
### Features
|
||||||
- New user viewer
|
- New user viewer
|
||||||
|
@ -62,20 +62,30 @@ class _EncryptionButtonState extends State<EncryptionButton> {
|
|||||||
.onSync
|
.onSync
|
||||||
.stream
|
.stream
|
||||||
.listen((s) => setState(() => null));
|
.listen((s) => setState(() => null));
|
||||||
return FutureBuilder<List<DeviceKeys>>(
|
return FutureBuilder<List<User>>(
|
||||||
future: widget.room.encrypted ? widget.room.getUserDeviceKeys() : null,
|
future:
|
||||||
|
widget.room.encrypted ? widget.room.requestParticipants() : null,
|
||||||
builder: (BuildContext context, snapshot) {
|
builder: (BuildContext context, snapshot) {
|
||||||
Color color;
|
Color color;
|
||||||
if (widget.room.encrypted && snapshot.hasData) {
|
if (widget.room.encrypted && snapshot.hasData) {
|
||||||
var data = snapshot.data;
|
final users = snapshot.data;
|
||||||
final deviceKeysList = data;
|
users.removeWhere((u) =>
|
||||||
color = Colors.orange;
|
!{Membership.invite, Membership.join}.contains(u.membership) ||
|
||||||
if (deviceKeysList.indexWhere((DeviceKeys deviceKeys) =>
|
!widget.room.client.userDeviceKeys.containsKey(u.id));
|
||||||
deviceKeys.verified == false &&
|
var allUsersValid = true;
|
||||||
deviceKeys.blocked == false) ==
|
var oneUserInvalid = false;
|
||||||
-1) {
|
for (final u in users) {
|
||||||
color = Colors.black.withGreen(220).withOpacity(0.75);
|
final status = widget.room.client.userDeviceKeys[u.id].verified;
|
||||||
|
if (status != UserVerifiedStatus.verified) {
|
||||||
|
allUsersValid = false;
|
||||||
}
|
}
|
||||||
|
if (status == UserVerifiedStatus.unknownDevice) {
|
||||||
|
oneUserInvalid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
color = oneUserInvalid
|
||||||
|
? Colors.red
|
||||||
|
: (allUsersValid ? Colors.green : Colors.orange);
|
||||||
} else if (!widget.room.encrypted &&
|
} else if (!widget.room.encrypted &&
|
||||||
widget.room.joinRules != JoinRules.public) {
|
widget.room.joinRules != JoinRules.public) {
|
||||||
color = null;
|
color = null;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:famedlysdk/famedlysdk.dart';
|
import 'package:famedlysdk/famedlysdk.dart';
|
||||||
|
import 'package:famedlysdk/encryption.dart';
|
||||||
import 'package:fluffychat/components/dialogs/simple_dialogs.dart';
|
import 'package:fluffychat/components/dialogs/simple_dialogs.dart';
|
||||||
import 'package:fluffychat/components/message_content.dart';
|
import 'package:fluffychat/components/message_content.dart';
|
||||||
import 'package:fluffychat/components/reply_content.dart';
|
import 'package:fluffychat/components/reply_content.dart';
|
||||||
@ -13,6 +14,8 @@ import '../avatar.dart';
|
|||||||
import '../matrix.dart';
|
import '../matrix.dart';
|
||||||
import '../message_reactions.dart';
|
import '../message_reactions.dart';
|
||||||
import 'state_message.dart';
|
import 'state_message.dart';
|
||||||
|
import '../../views/key_verification.dart';
|
||||||
|
import '../../utils/app_route.dart';
|
||||||
|
|
||||||
class Message extends StatelessWidget {
|
class Message extends StatelessWidget {
|
||||||
final Event event;
|
final Event event;
|
||||||
@ -37,6 +40,36 @@ class Message extends StatelessWidget {
|
|||||||
/// of touchscreen.
|
/// of touchscreen.
|
||||||
static bool useMouse = false;
|
static bool useMouse = false;
|
||||||
|
|
||||||
|
void _verifyOrRequestKey(BuildContext context) async {
|
||||||
|
final client = Matrix.of(context).client;
|
||||||
|
if (client.isUnknownSession && client.encryption.crossSigning.enabled) {
|
||||||
|
final req =
|
||||||
|
await client.userDeviceKeys[client.userID].startVerification();
|
||||||
|
req.onUpdate = () async {
|
||||||
|
if (req.state == KeyVerificationState.done) {
|
||||||
|
for (var i = 0; i < 12; i++) {
|
||||||
|
if (await client.encryption.keyManager.isCached()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await Future.delayed(Duration(seconds: 1));
|
||||||
|
}
|
||||||
|
final timeline = await event.room.getTimeline();
|
||||||
|
timeline.requestKeys();
|
||||||
|
timeline.cancelSubscriptions();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
await Navigator.of(context).push(
|
||||||
|
AppRoute.defaultRoute(
|
||||||
|
context,
|
||||||
|
KeyVerificationView(request: req),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await SimpleDialogs(context).tryRequestWithLoadingDialog(
|
||||||
|
event.getDisplayEvent(timeline).requestKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (event.type == EventTypes.Unknown) {
|
if (event.type == EventTypes.Unknown) {
|
||||||
@ -137,12 +170,13 @@ class Message extends StatelessWidget {
|
|||||||
RaisedButton(
|
RaisedButton(
|
||||||
color: color.withAlpha(100),
|
color: color.withAlpha(100),
|
||||||
child: Text(
|
child: Text(
|
||||||
L10n.of(context).requestPermission,
|
client.isUnknownSession &&
|
||||||
|
client.encryption.crossSigning.enabled
|
||||||
|
? L10n.of(context).verify
|
||||||
|
: L10n.of(context).requestPermission,
|
||||||
style: TextStyle(color: textColor),
|
style: TextStyle(color: textColor),
|
||||||
),
|
),
|
||||||
onPressed: () => SimpleDialogs(context)
|
onPressed: () => _verifyOrRequestKey(context),
|
||||||
.tryRequestWithLoadingDialog(
|
|
||||||
displayEvent.requestKey()),
|
|
||||||
),
|
),
|
||||||
SizedBox(height: 4),
|
SizedBox(height: 4),
|
||||||
Opacity(
|
Opacity(
|
||||||
|
@ -269,10 +269,21 @@ class MatrixState extends State<Matrix> {
|
|||||||
});
|
});
|
||||||
onKeyVerificationRequestSub ??= client.onKeyVerificationRequest.stream
|
onKeyVerificationRequestSub ??= client.onKeyVerificationRequest.stream
|
||||||
.listen((KeyVerification request) async {
|
.listen((KeyVerification request) async {
|
||||||
|
var hidPopup = false;
|
||||||
|
request.onUpdate = () {
|
||||||
|
if (!hidPopup &&
|
||||||
|
{KeyVerificationState.done, KeyVerificationState.error}
|
||||||
|
.contains(request.state)) {
|
||||||
|
Navigator.of(context, rootNavigator: true).pop('dialog');
|
||||||
|
}
|
||||||
|
hidPopup = true;
|
||||||
|
};
|
||||||
if (await SimpleDialogs(context).askConfirmation(
|
if (await SimpleDialogs(context).askConfirmation(
|
||||||
titleText: L10n.of(context).newVerificationRequest,
|
titleText: L10n.of(context).newVerificationRequest,
|
||||||
contentText: L10n.of(context).askVerificationRequest(request.userId),
|
contentText: L10n.of(context).askVerificationRequest(request.userId),
|
||||||
)) {
|
)) {
|
||||||
|
request.onUpdate = null;
|
||||||
|
hidPopup = true;
|
||||||
await request.acceptVerification();
|
await request.acceptVerification();
|
||||||
await Navigator.of(context).push(
|
await Navigator.of(context).push(
|
||||||
AppRoute.defaultRoute(
|
AppRoute.defaultRoute(
|
||||||
@ -281,6 +292,8 @@ class MatrixState extends State<Matrix> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
request.onUpdate = null;
|
||||||
|
hidPopup = true;
|
||||||
await request.rejectVerification();
|
await request.rejectVerification();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -12,6 +12,8 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||||||
import '../utils/presence_extension.dart';
|
import '../utils/presence_extension.dart';
|
||||||
import 'dialogs/simple_dialogs.dart';
|
import 'dialogs/simple_dialogs.dart';
|
||||||
import 'matrix.dart';
|
import 'matrix.dart';
|
||||||
|
import '../views/key_verification.dart';
|
||||||
|
import '../utils/app_route.dart';
|
||||||
|
|
||||||
class UserBottomSheet extends StatelessWidget {
|
class UserBottomSheet extends StatelessWidget {
|
||||||
final User user;
|
final User user;
|
||||||
@ -72,9 +74,22 @@ class UserBottomSheet extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _verifyAction(BuildContext context) async {
|
||||||
|
final client = Matrix.of(context).client;
|
||||||
|
final req = await client.userDeviceKeys[user.id].startVerification();
|
||||||
|
await Navigator.of(context).push(
|
||||||
|
AppRoute.defaultRoute(
|
||||||
|
context,
|
||||||
|
KeyVerificationView(request: req),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final presence = Matrix.of(context).client.presences[user.id];
|
final client = Matrix.of(context).client;
|
||||||
|
final presence = client.presences[user.id];
|
||||||
|
final verificationStatus = client.userDeviceKeys[user.id]?.verified;
|
||||||
var items = <PopupMenuEntry<String>>[];
|
var items = <PopupMenuEntry<String>>[];
|
||||||
|
|
||||||
if (onMention != null) {
|
if (onMention != null) {
|
||||||
@ -145,6 +160,21 @@ class UserBottomSheet extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
title: Text(user.calcDisplayname()),
|
title: Text(user.calcDisplayname()),
|
||||||
actions: [
|
actions: [
|
||||||
|
if (verificationStatus != null)
|
||||||
|
InkWell(
|
||||||
|
child: Icon(
|
||||||
|
Icons.lock,
|
||||||
|
color: {
|
||||||
|
UserVerifiedStatus.unknownDevice: Colors.red,
|
||||||
|
UserVerifiedStatus.verified: Colors.green,
|
||||||
|
}[verificationStatus] ??
|
||||||
|
Colors.orange,
|
||||||
|
),
|
||||||
|
onTap: () =>
|
||||||
|
verificationStatus == UserVerifiedStatus.unknown
|
||||||
|
? _verifyAction(context)
|
||||||
|
: null,
|
||||||
|
),
|
||||||
if (user.id != Matrix.of(context).client.userID)
|
if (user.id != Matrix.of(context).client.userID)
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
itemBuilder: (_) => items,
|
itemBuilder: (_) => items,
|
||||||
|
@ -146,8 +146,7 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
|
|||||||
.client
|
.client
|
||||||
.userDeviceKeys[deviceKeys[i].userId]
|
.userDeviceKeys[deviceKeys[i].userId]
|
||||||
.verified ==
|
.verified ==
|
||||||
UserVerifiedStatus.unknown &&
|
UserVerifiedStatus.unknown) {
|
||||||
deviceKeys[i].userId != room.client.userID) {
|
|
||||||
items.add(PopupMenuItem(
|
items.add(PopupMenuItem(
|
||||||
child: Text(L10n.of(context).verifyUser),
|
child: Text(L10n.of(context).verifyUser),
|
||||||
value: 'verify_user',
|
value: 'verify_user',
|
||||||
@ -211,7 +210,7 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
|
|||||||
},
|
},
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
"${deviceKeys[i].unsigned["device_display_name"] ?? L10n.of(context).unknownDevice} - ${deviceKeys[i].deviceId}",
|
'${deviceKeys[i].deviceDisplayName ?? L10n.of(context).unknownDevice} - ${deviceKeys[i].deviceId}',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: deviceKeys[i].blocked
|
color: deviceKeys[i].blocked
|
||||||
? Colors.red
|
? Colors.red
|
||||||
@ -220,9 +219,7 @@ class _ChatEncryptionSettingsState extends State<ChatEncryptionSettings> {
|
|||||||
: Colors.orange),
|
: Colors.orange),
|
||||||
),
|
),
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
deviceKeys[i]
|
deviceKeys[i].ed25519Key.beautified,
|
||||||
.keys['ed25519:${deviceKeys[i].deviceId}']
|
|
||||||
.beautified,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
|
46
pubspec.lock
46
pubspec.lock
@ -49,7 +49,7 @@ packages:
|
|||||||
name: async
|
name: async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.0-nullsafety.2"
|
version: "2.5.0-nullsafety.1"
|
||||||
base58check:
|
base58check:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -63,7 +63,7 @@ packages:
|
|||||||
name: boolean_selector
|
name: boolean_selector
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0-nullsafety.2"
|
version: "2.1.0-nullsafety.1"
|
||||||
bot_toast:
|
bot_toast:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -98,14 +98,14 @@ packages:
|
|||||||
name: characters
|
name: characters
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0-nullsafety.4"
|
version: "1.1.0-nullsafety.3"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: charcode
|
name: charcode
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0-nullsafety.2"
|
version: "1.2.0-nullsafety.1"
|
||||||
circular_check_box:
|
circular_check_box:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -126,14 +126,14 @@ packages:
|
|||||||
name: clock
|
name: clock
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0-nullsafety.2"
|
version: "1.1.0-nullsafety.1"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: collection
|
name: collection
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0-nullsafety.4"
|
version: "1.15.0-nullsafety.3"
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -203,13 +203,13 @@ packages:
|
|||||||
name: fake_async
|
name: fake_async
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0-nullsafety.2"
|
version: "1.2.0-nullsafety.1"
|
||||||
famedlysdk:
|
famedlysdk:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: "955fb747c29eab76b17eb9a13ebc15026e917fb8"
|
ref: "15d817023d34f813e95eba6ca8c71c575b8c2457"
|
||||||
resolved-ref: "955fb747c29eab76b17eb9a13ebc15026e917fb8"
|
resolved-ref: "15d817023d34f813e95eba6ca8c71c575b8c2457"
|
||||||
url: "https://gitlab.com/famedly/famedlysdk.git"
|
url: "https://gitlab.com/famedly/famedlysdk.git"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
@ -540,7 +540,7 @@ packages:
|
|||||||
name: matcher
|
name: matcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.10-nullsafety.2"
|
version: "0.12.10-nullsafety.1"
|
||||||
matrix_file_e2ee:
|
matrix_file_e2ee:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -561,7 +561,7 @@ packages:
|
|||||||
name: meta
|
name: meta
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0-nullsafety.5"
|
version: "1.3.0-nullsafety.3"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -661,7 +661,7 @@ packages:
|
|||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0-nullsafety.2"
|
version: "1.8.0-nullsafety.1"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -876,7 +876,7 @@ packages:
|
|||||||
name: source_span
|
name: source_span
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0-nullsafety.3"
|
version: "1.8.0-nullsafety.2"
|
||||||
sqflite:
|
sqflite:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -911,21 +911,21 @@ packages:
|
|||||||
name: stack_trace
|
name: stack_trace
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.0-nullsafety.5"
|
version: "1.10.0-nullsafety.1"
|
||||||
stream_channel:
|
stream_channel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: stream_channel
|
name: stream_channel
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0-nullsafety.2"
|
version: "2.1.0-nullsafety.1"
|
||||||
string_scanner:
|
string_scanner:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: string_scanner
|
name: string_scanner
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0-nullsafety.2"
|
version: "1.1.0-nullsafety.1"
|
||||||
swipe_to_action:
|
swipe_to_action:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -946,28 +946,28 @@ packages:
|
|||||||
name: term_glyph
|
name: term_glyph
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0-nullsafety.2"
|
version: "1.2.0-nullsafety.1"
|
||||||
test:
|
test:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test
|
name: test
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.16.0-nullsafety.7"
|
version: "1.16.0-nullsafety.5"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.19-nullsafety.4"
|
version: "0.2.19-nullsafety.2"
|
||||||
test_core:
|
test_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_core
|
name: test_core
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.12-nullsafety.7"
|
version: "0.3.12-nullsafety.5"
|
||||||
timezone:
|
timezone:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -981,7 +981,7 @@ packages:
|
|||||||
name: typed_data
|
name: typed_data
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0-nullsafety.4"
|
version: "1.3.0-nullsafety.3"
|
||||||
universal_html:
|
universal_html:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1065,7 +1065,7 @@ packages:
|
|||||||
name: vector_math
|
name: vector_math
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0-nullsafety.4"
|
version: "2.1.0-nullsafety.3"
|
||||||
vm_service:
|
vm_service:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1137,5 +1137,5 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.2"
|
version: "0.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.11.0-0.0 <=2.11.0-260.0.dev"
|
dart: ">=2.10.2 <=2.11.0-161.0.dev"
|
||||||
flutter: ">=1.22.2 <2.0.0"
|
flutter: ">=1.22.2 <2.0.0"
|
||||||
|
@ -23,7 +23,7 @@ dependencies:
|
|||||||
famedlysdk:
|
famedlysdk:
|
||||||
git:
|
git:
|
||||||
url: https://gitlab.com/famedly/famedlysdk.git
|
url: https://gitlab.com/famedly/famedlysdk.git
|
||||||
ref: 955fb747c29eab76b17eb9a13ebc15026e917fb8
|
ref: 15d817023d34f813e95eba6ca8c71c575b8c2457
|
||||||
|
|
||||||
localstorage: ^3.0.3+6
|
localstorage: ^3.0.3+6
|
||||||
file_picker_cross: 4.2.2
|
file_picker_cross: 4.2.2
|
||||||
|
Loading…
Reference in New Issue
Block a user