Compare commits

...
33 Commits
Author SHA1 Message Date
brandon.presley 11de9ccfe8 Turn off autocorrect/capitalization for login 2023-12-30 16:17:05 +13:00
brandon.presley 128a1bf35f Add persistence to matrix Client 2023-12-30 16:11:20 +13:00
brandon.presley a552c30ba0 Remove logging of rooms with "Group with" 2023-12-30 16:00:54 +13:00
brandon.presley 2039b4e90d Remove custom code for renaming groups
Was something for my local matrix
environment, committed
accidentally.
2023-12-30 15:54:30 +13:00
brandon.presley 1aa73bbd7e Add failed attempt to fix all room names 2023-12-30 15:16:42 +13:00
brandon.presley 6ca8f646b2 Fix null being on chat messages author temporarily 2023-12-30 14:44:35 +13:00
brandon.presley a40dd74b96 Close chat faster on send 2023-12-30 12:49:14 +13:00
brandon.presley 10b84d1521 Hide "Group with" from room names 2023-12-30 12:47:48 +13:00
brandon.presley 8ca788ccdd Add button to change image of chat 2023-12-30 12:41:40 +13:00
brandon.presley 7c1e18c4e8 Add avatar to rooms 2023-12-30 11:47:27 +13:00
brandon.presley 76411aa827 Fix login flow and room name updates
1. Logging in with saved credentials should
just go straight to rooms page without
flickering the login page
2. Rooms should be updated if an event
fires for their names changing
2023-12-30 11:39:34 +13:00
brandon.presley 27b7f79d74 Only login if the client is logged out 2023-12-30 11:23:57 +13:00
brandon.presley f229c0371b Save username/password instead of tokens
The tokens never worked. I don't know what i'm doing wrong but fluffychat basically just logs in and never tries to save anything. Im not sure how im meant to do this.
2023-12-30 11:17:21 +13:00
brandon.presley 1cd8ba41ae Reduce nesting of login.dart 2023-12-30 11:16:38 +13:00
brandon.presley 3728172844 Tried to set up auth tokens
I always get invalid token error
when I try to login with mLoginToken type
2023-12-29 19:31:35 +13:00
brandon.presley ea7587fe2d Only show messages in room view 2023-12-29 17:19:40 +13:00
brandon.presley 1376580e7e Add profile images to chats 2023-12-29 17:10:47 +13:00
brandon.presley 5fbc8bee42 Add launch.json but debugging still doesnt work 2023-12-29 15:12:36 +13:00
brandon.presley 9c93ea1008 Move loading into fab button of login 2023-12-29 15:12:10 +13:00
brandon.presley e478552587 Add log out button 2023-12-29 14:50:09 +13:00
brandon.presley 5c4526f1fc Add loader for logging in 2023-12-29 14:50:00 +13:00
brandon.presley 302f46804e Include internet permissions in manifest 2023-12-29 11:31:33 +13:00
brandon.presley 071df18b81 Wait for rooms and account data to load 2023-12-29 11:31:26 +13:00
brandon.presley c5ae0f0e85 Add ability to send messages 2023-12-29 11:31:18 +13:00
brandon.presley 705aeb568f Remove unused code 2023-12-29 10:53:43 +13:00
brandon.presley 3352939009 View messages for a room 2023-12-29 10:40:09 +13:00
brandon.presley a6306ceddc List rooms in chats.dart 2023-12-29 09:20:50 +13:00
brandon.presley 112fe960be Update analysis options 2023-12-29 09:20:28 +13:00
brandon.presley 799c15321b Pretty sure this is just some EOL stuff 2023-12-28 18:53:21 +13:00
brandon.presley b636eaaa57 Add shared preferences and chats page 2023-12-28 17:48:18 +13:00
brandon.presley 02ab48521e Change title of material app to Zenith 2023-12-28 16:45:17 +13:00
brandon.presley d5e7316ee8 Add todos in README.md 2023-12-28 16:44:45 +13:00
brandon.presley 53203e3527 Add template for README 2023-12-28 16:44:08 +13:00
90 changed files with 5092 additions and 4540 deletions
+25
View File
@@ -0,0 +1,25 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "zenith",
"request": "launch",
"type": "dart",
"cwd": "${workspaceFolder}",
"program": "${workspaceFolder}/lib/main.dart",
"flutterMode": "debug"
},
{
"name": "zenith (profile mode)",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "zenith (release mode)",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
]
}
+20 -10
View File
@@ -1,16 +1,26 @@
# zenith
# Zenith
A new Flutter project.
A matrix client for Android.
## Getting Started
TODO: Add play store + f-droid store links.
This project is a starting point for a Flutter application.
# Screenshots
A few resources to get you started if this is your first Flutter project:
TODO: Add screenshots.
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
# Features
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
TODO: Add feature list.
# How to Build
First install dependencies:
```
flutter pub get
```
Then build the apk:
```
flutter build apk
```
+2
View File
@@ -23,6 +23,8 @@ linter:
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
curly_braces_in_flow_control_structures: false
avoid_print: false
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
+2
View File
@@ -1,4 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:label="zenith"
android:name="${applicationName}"
+88 -47
View File
@@ -1,5 +1,9 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:zenith/rooms.dart';
import 'package:zenith/zenith_client_provider.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@@ -12,26 +16,53 @@ class _LoginPageState extends State<LoginPage> {
final serverController = TextEditingController();
final usernameController = TextEditingController();
final passwordController = TextEditingController();
bool loggingIn = false;
String failedMessage = '';
void connectMatrix() async {
debugPrint("Making the client...");
final client = Client("zenith");
client.onLoginStateChanged.stream.listen((event) {
debugPrint("LoginState=${event.toString()}");
void login() async {
setState(() {
loggingIn = true;
});
debugPrint("Checking the homeserver...");
await client.checkHomeserver(Uri.parse(serverController.text));
final provider = Provider.of<ZenithClientProvider>(context, listen: false);
debugPrint("Logging in...");
await client.login(LoginType.mLoginPassword,
try {
await provider.client.checkHomeserver(Uri.parse(serverController.text));
await provider.client.login(
LoginType.mLoginPassword,
password: passwordController.text,
identifier: AuthenticationUserIdentifier(user: usernameController.text),
password: passwordController.text);
refreshToken: true,
);
} catch (error) {
print(error);
setState(() {
failedMessage = error.toString();
loggingIn = false;
});
for (var room in client.rooms) {
debugPrint(
"Room ID: ${room.id}, Room Name: ${room.getLocalizedDisplayname()}");
Future.delayed(const Duration(seconds: 10), () {
setState(() {
failedMessage = '';
});
});
return;
} finally {
setState(() {
loggingIn = false;
});
}
final prefs = await SharedPreferences.getInstance();
prefs.setString("homeserver", serverController.text);
prefs.setString("username", usernameController.text);
prefs.setString("password", passwordController.text);
if (!mounted) return;
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => const RoomsPage()),
(route) => false);
}
@override
@@ -41,41 +72,51 @@ class _LoginPageState extends State<LoginPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("Login"),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
controller: serverController,
decoration: const InputDecoration(
labelText: 'Server', hintText: 'https://matrix.org'),
),
TextFormField(
controller: usernameController,
decoration: const InputDecoration(
labelText: 'Username', hintText: 'john'),
),
TextFormField(
controller: passwordController,
decoration: const InputDecoration(labelText: 'Password'),
obscureText: true,
),
],
return Consumer<ZenithClientProvider>(
builder: (context, provider, child) => Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("Login"),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: connectMatrix,
tooltip: 'Log in',
child: const Icon(Icons.login),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
controller: serverController,
autocorrect: false,
textCapitalization: TextCapitalization.none,
decoration: const InputDecoration(
labelText: 'Server', hintText: 'https://matrix.org'),
),
TextFormField(
controller: usernameController,
autocorrect: false,
decoration: const InputDecoration(
labelText: 'Username', hintText: 'john'),
),
TextFormField(
controller: passwordController,
autocorrect: false,
decoration: const InputDecoration(labelText: 'Password'),
obscureText: true,
onFieldSubmitted: (value) => login(),
),
Text(failedMessage,
style: Theme.of(context).textTheme.headlineSmall)
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: loggingIn ? null : login,
tooltip: 'Log in',
child: loggingIn
? const CircularProgressIndicator()
: const Icon(Icons.login),
)),
);
}
}
+63 -7
View File
@@ -1,5 +1,10 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:zenith/rooms.dart';
import 'package:zenith/login.dart';
import 'package:provider/provider.dart';
import 'package:zenith/zenith_client_provider.dart';
void main() {
runApp(const MyApp());
@@ -10,13 +15,16 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
return ChangeNotifierProvider(
create: (context) => ZenithClientProvider(),
child: MaterialApp(
title: 'Zenith',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(),
),
home: const MyHomePage(),
);
}
}
@@ -29,13 +37,61 @@ class MyHomePage extends StatefulWidget {
}
class _MyHomePageState extends State<MyHomePage> {
bool showRooms = false;
String errorMessage = "";
void getCreds() async {
final prefs = await SharedPreferences.getInstance();
final homeserver = prefs.getString("homeserver");
final username = prefs.getString("username");
final password = prefs.getString("password");
if (homeserver == null || username == null || password == null) return;
if (!mounted) return;
final provider = Provider.of<ZenithClientProvider>(context, listen: false);
setState(() {
showRooms = true;
});
try {
await provider.client.checkHomeserver(Uri.parse(homeserver));
if (!provider.client.isLogged())
await provider.client.login(LoginType.mLoginPassword,
password: password,
identifier: AuthenticationUserIdentifier(user: username));
} catch (error) {
print(error);
setState(() {
errorMessage = error.toString();
});
}
}
@override
void initState() {
super.initState();
getCreds();
}
@override
Widget build(BuildContext context) {
return const LoginPage();
if (errorMessage.isNotEmpty)
return Column(
children: [
ElevatedButton(
onPressed: () {
setState(() {
errorMessage = "";
});
},
child: const Text("Clear")),
ErrorWidget(errorMessage),
],
);
if (showRooms)
return const RoomsPage();
else
return const LoginPage();
}
}
+118
View File
@@ -0,0 +1,118 @@
import 'dart:async';
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
class RoomPage extends StatefulWidget {
const RoomPage({super.key, required this.room});
final Room room;
@override
State<RoomPage> createState() => _RoomPageState();
}
class _RoomPageState extends State<RoomPage> {
Timeline? timeline;
final chatController = TextEditingController();
StreamSubscription? updateListener;
void updateTimeline() async {
final newTimeline =
await widget.room.getTimeline(eventContextId: widget.room.fullyRead);
setState(() {
timeline = newTimeline;
});
}
@override
void initState() {
super.initState();
updateTimeline();
updateListener = widget.room.onUpdate.stream.listen((event) {
updateTimeline();
});
}
void setAvatar() async {
final participants = widget.room.getParticipants();
final picked = await FilePicker.platform.pickFiles(type: FileType.image);
if (picked == null) return;
final file = File(picked.files.single.path!);
final bytes = await file.readAsBytes();
final uri = await widget.room.client.uploadContent(bytes);
await widget.room.client.setAvatarUrl(participants[0].id, uri);
}
@override
void dispose() {
super.dispose();
updateListener?.cancel();
}
void sendMessage() {}
List<Event>? get messages => timeline?.events
.where((element) => element.type == EventTypes.Message)
.toList();
List<Widget> getChildren() {
if (timeline == null) return [const CircularProgressIndicator()];
return [
Expanded(
child: ListView.builder(
itemCount: messages?.length,
reverse: true,
itemBuilder: (context, index) => ListTile(
title: Text(
messages![index].senderFromMemoryOrFallback.displayName ??
""),
subtitle: Text(messages![index].body),
leading:
messages![index].senderFromMemoryOrFallback.avatarUrl !=
null
? CircleAvatar(
foregroundImage: NetworkImage(messages![index]
.senderFromMemoryOrFallback
.avatarUrl!
.getThumbnail(widget.room.client,
width: 50, height: 50)
.toString()))
: null,
)),
),
TextFormField(
controller: chatController,
textCapitalization: TextCapitalization.sentences,
textInputAction: TextInputAction.send,
decoration: const InputDecoration(hintText: 'Message'),
onFieldSubmitted: (value) async {
chatController.text = '';
await widget.room.sendTextEvent(value);
},
),
];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.room
.getLocalizedDisplayname()
.replaceFirst(RegExp("Group with "), "")),
actions: [
IconButton(onPressed: setAvatar, icon: const Icon(Icons.image))
],
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: getChildren(),
)),
);
}
}
+113
View File
@@ -0,0 +1,113 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:zenith/login.dart';
import 'package:zenith/room.dart';
import 'package:zenith/zenith_client_provider.dart';
class RoomsPage extends StatefulWidget {
const RoomsPage({super.key});
@override
State<RoomsPage> createState() => _RoomsPageState();
}
class _RoomsPageState extends State<RoomsPage> {
bool loadingRooms = true;
@override
void initState() {
super.initState();
}
void sendMessage() {}
void viewRoom(Room room) {
Navigator.push(
context, MaterialPageRoute(builder: (context) => RoomPage(room: room)));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("Zenith"),
actions: [
IconButton(
onPressed: () {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text("Log out?"),
actions: [
ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("Cancel")),
ElevatedButton(
child: const Text("Confirm"),
onPressed: () async {
print("Logging out...");
final prefs = await SharedPreferences.getInstance();
prefs.remove("homeserver");
prefs.remove("username");
prefs.remove("password");
if (!mounted) return;
final provider = Provider.of<ZenithClientProvider>(
context,
listen: false);
await provider.client.logout();
if (!mounted) return;
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => const LoginPage()),
(route) => false);
},
)
],
),
);
},
icon: const Icon(Icons.logout))
],
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Center(child: Consumer<ZenithClientProvider>(
builder: (context, provider, child) {
provider.client.onRoomState.stream.listen((event) {
setState(() {
loadingRooms = false;
});
});
if (provider.client.rooms.isEmpty)
return const CircularProgressIndicator();
else
return ListView.builder(
itemCount: provider.client.rooms.length,
itemBuilder: (context, index) => ListTile(
title: Text(provider.client.rooms[index]
.getLocalizedDisplayname()
.replaceFirst(RegExp("Group with "), "")),
leading: CircleAvatar(
foregroundImage: NetworkImage(provider
.client.rooms[index].avatar
?.getThumbnail(provider.client,
width: 50, height: 50)
.toString() ??
"")),
onTap: () => viewRoom(provider.client.rooms[index]),
));
},
)),
),
);
}
}
+13
View File
@@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:path_provider/path_provider.dart';
class ZenithClientProvider extends ChangeNotifier {
final client = Client("zenith", databaseBuilder: (_) async {
final dir =
await getApplicationSupportDirectory(); // Recommend path_provider package
final db = HiveCollectionsDatabase('matrix_example_chat', dir.path);
await db.open();
return db;
});
}
@@ -5,6 +5,10 @@
import FlutterMacOS
import Foundation
import path_provider_foundation
import shared_preferences_foundation
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
}
+166 -1
View File
@@ -137,6 +137,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.0"
file:
dependency: transitive
description:
name: file
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
file_picker:
dependency: "direct main"
description:
name: file_picker
sha256: "4e42aacde3b993c5947467ab640882c56947d9d27342a5b6f2895b23956954a6"
url: "https://pub.dev"
source: hosted
version: "6.1.1"
flutter:
dependency: "direct main"
description: flutter
@@ -166,11 +182,24 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.3.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da
url: "https://pub.dev"
source: hosted
version: "2.0.17"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
hive:
dependency: transitive
description:
@@ -315,6 +344,54 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.8.3"
path_provider:
dependency: "direct main"
description:
name: path_provider
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
url: "https://pub.dev"
source: hosted
version: "2.1.1"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668"
url: "https://pub.dev"
source: hosted
version: "2.2.2"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
petitparser:
dependency: transitive
description:
@@ -323,6 +400,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.4.0"
platform:
dependency: transitive
description:
name: platform
sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
url: "https://pub.dev"
source: hosted
version: "3.1.3"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
url: "https://pub.dev"
source: hosted
version: "2.1.7"
pointycastle:
dependency: transitive
description:
@@ -355,6 +448,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.3.2"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
url: "https://pub.dev"
source: hosted
version: "2.2.2"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
url: "https://pub.dev"
source: hosted
version: "2.2.1"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7"
url: "https://pub.dev"
source: hosted
version: "2.3.4"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
url: "https://pub.dev"
source: hosted
version: "2.3.1"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf
url: "https://pub.dev"
source: hosted
version: "2.2.1"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
sky_engine:
dependency: transitive
description: flutter
@@ -472,6 +621,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.2"
win32:
dependency: transitive
description:
name: win32
sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
url: "https://pub.dev"
source: hosted
version: "5.1.1"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
url: "https://pub.dev"
source: hosted
version: "1.0.3"
xml:
dependency: transitive
description:
@@ -482,4 +647,4 @@ packages:
version: "6.3.0"
sdks:
dart: ">=3.1.5 <4.0.0"
flutter: ">=1.20.0"
flutter: ">=3.10.0"
+3
View File
@@ -39,6 +39,9 @@ dependencies:
flutter_olm: ^1.4.1
flutter_openssl_crypto: ^0.3.0
provider: ^6.1.1
shared_preferences: ^2.2.2
file_picker: ^6.1.1
path_provider: ^2.1.1
dev_dependencies:
flutter_test: