mirror of
https://github.com/Linloir/Simple-TCP-Client.git
synced 2025-12-18 17:28:11 +08:00
More Codes
- Local Service Repository
This commit is contained in:
parent
2bbad43739
commit
d3a5a32fdb
@ -1,6 +1,8 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
sqfliteFfiInit();
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-11 10:30:05
|
* @Date : 2022-10-11 10:30:05
|
||||||
* @LastEditTime : 2022-10-11 15:36:23
|
* @LastEditTime : 2022-10-11 23:35:45
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -32,6 +32,7 @@ class Message extends JSONEncodable {
|
|||||||
final String _content;
|
final String _content;
|
||||||
final int _timestamp;
|
final int _timestamp;
|
||||||
late final String _contentmd5;
|
late final String _contentmd5;
|
||||||
|
late final String? _filemd5;
|
||||||
final LocalFile? _payload;
|
final LocalFile? _payload;
|
||||||
|
|
||||||
Message({
|
Message({
|
||||||
@ -54,6 +55,7 @@ class Message extends JSONEncodable {
|
|||||||
..addAll(Uint8List(4)..buffer.asInt32List()[0] = targetid)
|
..addAll(Uint8List(4)..buffer.asInt32List()[0] = targetid)
|
||||||
..addAll(Uint8List(4)..buffer.asInt32List()[0] = _timestamp)
|
..addAll(Uint8List(4)..buffer.asInt32List()[0] = _timestamp)
|
||||||
).toString();
|
).toString();
|
||||||
|
_filemd5 = _payload?.filemd5;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message.fromJSONObject({
|
Message.fromJSONObject({
|
||||||
@ -66,6 +68,7 @@ class Message extends JSONEncodable {
|
|||||||
_content = jsonObject['content'] as String,
|
_content = jsonObject['content'] as String,
|
||||||
_timestamp = jsonObject['timestamp'] as int,
|
_timestamp = jsonObject['timestamp'] as int,
|
||||||
_contentmd5 = jsonObject['md5encoded'] as String,
|
_contentmd5 = jsonObject['md5encoded'] as String,
|
||||||
|
_filemd5 = jsonObject['filemd5'] as String,
|
||||||
_payload = payload;
|
_payload = payload;
|
||||||
|
|
||||||
int get senderID => _userid;
|
int get senderID => _userid;
|
||||||
@ -75,6 +78,7 @@ class Message extends JSONEncodable {
|
|||||||
String get contentEncoded => _content;
|
String get contentEncoded => _content;
|
||||||
String get contentmd5 => _contentmd5;
|
String get contentmd5 => _contentmd5;
|
||||||
int get timeStamp => _timestamp;
|
int get timeStamp => _timestamp;
|
||||||
|
String? get filemd5 => _filemd5;
|
||||||
LocalFile? get payload => _payload;
|
LocalFile? get payload => _payload;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@ -1,6 +1,250 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-11 10:56:02
|
* @Date : 2022-10-11 10:56:02
|
||||||
* @LastEditTime : 2022-10-11 10:56:03
|
* @LastEditTime : 2022-10-11 23:49:16
|
||||||
* @Description :
|
* @Description : Local Service Repository
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:crypto/crypto.dart';
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:tcp_client/repositories/common_models/message.dart';
|
||||||
|
import 'package:tcp_client/repositories/common_models/userinfo.dart';
|
||||||
|
import 'package:tcp_client/repositories/local_service_repository/models/local_file.dart';
|
||||||
|
import 'package:sqflite_common/sqlite_api.dart';
|
||||||
|
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||||
|
|
||||||
|
class LocalServiceRepository {
|
||||||
|
late final Database _database;
|
||||||
|
|
||||||
|
LocalServiceRepository._internal({
|
||||||
|
required Database database
|
||||||
|
}): _database = database;
|
||||||
|
|
||||||
|
static FutureOr<void> _onDatabaseCreate(Database db, int version) async {
|
||||||
|
await db.execute(
|
||||||
|
'''
|
||||||
|
create table users (
|
||||||
|
userid integer primary key,
|
||||||
|
username text not null,
|
||||||
|
avatar text
|
||||||
|
);
|
||||||
|
create table msgs (
|
||||||
|
userid integer not null,
|
||||||
|
targetid integer not null,
|
||||||
|
contenttype text not null,
|
||||||
|
content text not null,
|
||||||
|
timestamp int not null,
|
||||||
|
md5encoded text primary key,
|
||||||
|
filemd5 text not null
|
||||||
|
);
|
||||||
|
create table files (
|
||||||
|
filemd5 text primary key,
|
||||||
|
dir text not null
|
||||||
|
);
|
||||||
|
'''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<LocalServiceRepository> create({
|
||||||
|
UserInfo? currentUser,
|
||||||
|
required String databaseFilePath
|
||||||
|
}) async {
|
||||||
|
var database = await databaseFactoryFfi.openDatabase(
|
||||||
|
databaseFilePath,
|
||||||
|
options: OpenDatabaseOptions(
|
||||||
|
version: 1,
|
||||||
|
onCreate: _onDatabaseCreate
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return LocalServiceRepository._internal(database: database);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Calls on the system to open the file
|
||||||
|
Future<LocalFile?> pickFile(FileType fileType) async {
|
||||||
|
var filePickResult = await FilePicker.platform.pickFiles(
|
||||||
|
type: fileType,
|
||||||
|
allowMultiple: false,
|
||||||
|
);
|
||||||
|
if (filePickResult == null) return null;
|
||||||
|
var file = File(filePickResult.files.single.path!);
|
||||||
|
return LocalFile(
|
||||||
|
file: file,
|
||||||
|
filemd5: md5.convert(await file.readAsBytes()).toString(),
|
||||||
|
ext: file.path.substring(file.path.lastIndexOf('.'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> storeMessages(List<Message> messages) async {
|
||||||
|
for(var message in messages) {
|
||||||
|
try {
|
||||||
|
await _database.insert(
|
||||||
|
'msgs',
|
||||||
|
message.jsonObject,
|
||||||
|
conflictAlgorithm: ConflictAlgorithm.replace
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
//TODO: do something
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Message>> findMessages({required String pattern}) async {
|
||||||
|
// Obtain shared preferences.
|
||||||
|
final pref = await SharedPreferences.getInstance();
|
||||||
|
// Get user info from preferences
|
||||||
|
var currentUserID = pref.getInt('userid');
|
||||||
|
var alikeMessages = await _database.query(
|
||||||
|
'msgs',
|
||||||
|
where: 'userid = ? or targetid = ?',
|
||||||
|
whereArgs: [
|
||||||
|
currentUserID, currentUserID
|
||||||
|
],
|
||||||
|
orderBy: 'timestamp desc',
|
||||||
|
limit: 100
|
||||||
|
);
|
||||||
|
return alikeMessages.map((e) => Message.fromJSONObject(jsonObject: e)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Find the most recent message of given users
|
||||||
|
Future<List<List<Message>>> fetchMessageList({required List<int> users}) async {
|
||||||
|
var pref = await SharedPreferences.getInstance();
|
||||||
|
var currentUserID = pref.getInt('userid');
|
||||||
|
var messages = <List<Message>>[];
|
||||||
|
for(var user in users) {
|
||||||
|
var queryResult = await _database.query(
|
||||||
|
'msgs',
|
||||||
|
where: '(userid = ? and targetid = ?) and (userid = ? and targetid = ?)',
|
||||||
|
whereArgs: [
|
||||||
|
currentUserID, user, user, currentUserID
|
||||||
|
],
|
||||||
|
orderBy: 'timestamp desc',
|
||||||
|
limit: 1
|
||||||
|
);
|
||||||
|
if(queryResult.isEmpty) {
|
||||||
|
messages.add([]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
messages.add([Message.fromJSONObject(jsonObject: queryResult[0])]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fetch chat history with another user, provided the user ID
|
||||||
|
Future<List<Message>> fetchMessageHistory({required int userID, required int position, int num = 20}) async {
|
||||||
|
//the histories with userID
|
||||||
|
var pref = await SharedPreferences.getInstance();
|
||||||
|
var currentUserID = pref.getInt('userid');
|
||||||
|
if(currentUserID == null) {
|
||||||
|
//TODO: do something
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
var queryResult = await _database.query(
|
||||||
|
'msgs',
|
||||||
|
where: '(userid = ? and targetid = ?) or (userid = ? and targetid = ?)',
|
||||||
|
whereArgs: [
|
||||||
|
currentUserID, userID, userID, currentUserID
|
||||||
|
],
|
||||||
|
orderBy: 'timestamp desc',
|
||||||
|
limit: num,
|
||||||
|
offset: position
|
||||||
|
);
|
||||||
|
return queryResult.map((e) => Message.fromJSONObject(jsonObject: e)).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<File?> findFile({required String filemd5}) async {
|
||||||
|
var directory = await _database.query(
|
||||||
|
'files',
|
||||||
|
where: 'filemd5 = ?',
|
||||||
|
whereArgs: [
|
||||||
|
filemd5
|
||||||
|
]
|
||||||
|
);
|
||||||
|
if(directory.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var filePath = directory[0]['dir'] as String;
|
||||||
|
//Try if the file exists
|
||||||
|
var file = File(filePath);
|
||||||
|
if(await file.exists()) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//Delete all linked files
|
||||||
|
await _database.delete(
|
||||||
|
'files',
|
||||||
|
where: 'filemd5 = ?',
|
||||||
|
whereArgs: [
|
||||||
|
filemd5
|
||||||
|
]
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> storeFile({
|
||||||
|
required LocalFile tempFile
|
||||||
|
}) async {
|
||||||
|
//Write to file library
|
||||||
|
var documentPath = (await getApplicationDocumentsDirectory()).path;
|
||||||
|
var permanentFilePath = '$documentPath/files/${tempFile.filemd5}${tempFile.ext}';
|
||||||
|
await tempFile.file.copy(permanentFilePath);
|
||||||
|
await _database.insert(
|
||||||
|
'files',
|
||||||
|
{
|
||||||
|
'filemd5': tempFile.filemd5,
|
||||||
|
'dir': permanentFilePath
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final StreamController<UserInfo> _userInfoChangeStreamController = StreamController();
|
||||||
|
Stream<UserInfo> get userInfoChangedStream => _userInfoChangeStreamController.stream;
|
||||||
|
|
||||||
|
Future<void> storeUserInfo({
|
||||||
|
required UserInfo userInfo
|
||||||
|
}) async {
|
||||||
|
//check if exist
|
||||||
|
var queryResult = await _database.query(
|
||||||
|
'users',
|
||||||
|
where: 'userid = ?',
|
||||||
|
whereArgs: [userInfo.userID]
|
||||||
|
);
|
||||||
|
if(queryResult.isEmpty) {
|
||||||
|
_database.insert(
|
||||||
|
'users',
|
||||||
|
userInfo.jsonObject
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_database.update(
|
||||||
|
'users',
|
||||||
|
userInfo.jsonObject,
|
||||||
|
where: 'userid = ?',
|
||||||
|
whereArgs: [userInfo.userID]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_userInfoChangeStreamController.add(userInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<UserInfo?> fetchUserInfo({required userid}) async {
|
||||||
|
var targetUser = await _database.query(
|
||||||
|
'users',
|
||||||
|
where: 'userid = ?',
|
||||||
|
whereArgs: [userid]
|
||||||
|
);
|
||||||
|
if(targetUser.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return UserInfo.fromJSONObject(jsonObject: targetUser[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-11 10:55:36
|
* @Date : 2022-10-11 10:55:36
|
||||||
* @LastEditTime : 2022-10-11 17:44:06
|
* @LastEditTime : 2022-10-11 22:54:59
|
||||||
* @Description : Local File Model
|
* @Description : Local File Model
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -12,8 +12,9 @@ import 'package:equatable/equatable.dart';
|
|||||||
class LocalFile extends Equatable {
|
class LocalFile extends Equatable {
|
||||||
final File file;
|
final File file;
|
||||||
final String filemd5;
|
final String filemd5;
|
||||||
|
final String ext;
|
||||||
|
|
||||||
const LocalFile({required this.file, required this.filemd5});
|
const LocalFile({required this.file, required this.filemd5, required this.ext});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [filemd5];
|
List<Object> get props => [filemd5];
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-11 11:02:19
|
* @Date : 2022-10-11 11:02:19
|
||||||
* @LastEditTime : 2022-10-11 16:00:53
|
* @LastEditTime : 2022-10-11 22:55:48
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -195,12 +195,18 @@ class FindFileResponse extends TCPResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FetchFileResponse extends TCPResponse {
|
class FetchFileResponse extends TCPResponse {
|
||||||
final LocalFile _payload;
|
late final LocalFile _payload;
|
||||||
|
|
||||||
FetchFileResponse({
|
FetchFileResponse({
|
||||||
required Map<String, Object?> jsonObject,
|
required Map<String, Object?> jsonObject,
|
||||||
required LocalFile payload
|
required LocalFile payload
|
||||||
}): _payload = payload, super(jsonObject: jsonObject);
|
}): super(jsonObject: jsonObject) {
|
||||||
|
_payload = LocalFile(
|
||||||
|
file: payload.file,
|
||||||
|
filemd5: payload.filemd5,
|
||||||
|
ext: (jsonObject['body'] as Map<String, Object?>)['ext'] as String
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
LocalFile get payload => _payload;
|
LocalFile get payload => _payload;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-11 09:42:05
|
* @Date : 2022-10-11 09:42:05
|
||||||
* @LastEditTime : 2022-10-11 17:41:11
|
* @LastEditTime : 2022-10-11 22:55:28
|
||||||
* @Description : TCP repository
|
* @Description : TCP repository
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -11,12 +11,17 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:tcp_client/repositories/local_service_repository/models/local_file.dart';
|
import 'package:tcp_client/repositories/local_service_repository/models/local_file.dart';
|
||||||
import 'package:tcp_client/repositories/tcp_repository/models/tcp_request.dart';
|
import 'package:tcp_client/repositories/tcp_repository/models/tcp_request.dart';
|
||||||
import 'package:tcp_client/repositories/tcp_repository/models/tcp_response.dart';
|
import 'package:tcp_client/repositories/tcp_repository/models/tcp_response.dart';
|
||||||
|
|
||||||
class TCPRepository {
|
class TCPRepository {
|
||||||
TCPRepository(this._socket) {
|
TCPRepository({
|
||||||
|
required Socket socket,
|
||||||
|
required String remoteAddress,
|
||||||
|
required int remotePort
|
||||||
|
}): _socket = socket, _remoteAddress = remoteAddress, _remotePort = remotePort {
|
||||||
_socket.listen(_pullResponse);
|
_socket.listen(_pullResponse);
|
||||||
//This future never ends, would that be bothersome?
|
//This future never ends, would that be bothersome?
|
||||||
Future(() async {
|
Future(() async {
|
||||||
@ -33,6 +38,8 @@ class TCPRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Socket _socket;
|
final Socket _socket;
|
||||||
|
final String _remoteAddress;
|
||||||
|
final int _remotePort;
|
||||||
|
|
||||||
//Stores the incoming bytes of the TCP connection temporarily
|
//Stores the incoming bytes of the TCP connection temporarily
|
||||||
final List<int> buffer = [];
|
final List<int> buffer = [];
|
||||||
@ -50,7 +57,12 @@ class TCPRepository {
|
|||||||
|
|
||||||
//Provide a response stream for blocs to listen on
|
//Provide a response stream for blocs to listen on
|
||||||
final StreamController<TCPResponse> _responseStreamController = StreamController();
|
final StreamController<TCPResponse> _responseStreamController = StreamController();
|
||||||
|
Stream<TCPResponse>? _responseStreamBroadcast;
|
||||||
Stream<TCPResponse> get responseStream => _responseStreamController.stream;
|
Stream<TCPResponse> get responseStream => _responseStreamController.stream;
|
||||||
|
Stream<TCPResponse> get responseStreamBroadcast {
|
||||||
|
_responseStreamBroadcast ??= _responseStreamController.stream.asBroadcastStream();
|
||||||
|
return _responseStreamBroadcast!;
|
||||||
|
}
|
||||||
|
|
||||||
//Provide a request stream for widgets to push to
|
//Provide a request stream for widgets to push to
|
||||||
final StreamController<TCPRequest> _requestStreamController = StreamController();
|
final StreamController<TCPRequest> _requestStreamController = StreamController();
|
||||||
@ -213,7 +225,8 @@ class TCPRepository {
|
|||||||
jsonObject: responseObject,
|
jsonObject: responseObject,
|
||||||
payload: LocalFile(
|
payload: LocalFile(
|
||||||
file: tempFile,
|
file: tempFile,
|
||||||
filemd5: md5.convert(await tempFile.readAsBytes()).toString()
|
filemd5: md5.convert(await tempFile.readAsBytes()).toString(),
|
||||||
|
ext: ""
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
@ -239,4 +252,37 @@ class TCPRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> checkFileExistence({
|
||||||
|
required LocalFile file
|
||||||
|
}) async {
|
||||||
|
//Duplicate current socket
|
||||||
|
Socket socket = await Socket.connect(_remoteAddress, _remotePort);
|
||||||
|
TCPRepository duplicatedRepository = TCPRepository(
|
||||||
|
socket: socket,
|
||||||
|
remoteAddress: _remoteAddress,
|
||||||
|
remotePort: _remotePort
|
||||||
|
);
|
||||||
|
var pref = await SharedPreferences.getInstance();
|
||||||
|
var request = FindFileRequest(file: file, token: pref.getInt('token')!);
|
||||||
|
duplicatedRepository.pushRequest(request);
|
||||||
|
var hasFile = false;
|
||||||
|
await for(var response in duplicatedRepository.responseStream) {
|
||||||
|
if(response.type == TCPResponseType.findFile) {
|
||||||
|
hasFile = response.status == TCPResponseStatus.ok;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
duplicatedRepository.dispose();
|
||||||
|
return hasFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
_responseRawStreamController.close();
|
||||||
|
_payloadPullStreamController.close();
|
||||||
|
_payloadRawStreamController.close();
|
||||||
|
_responseStreamController.close();
|
||||||
|
_requestStreamController.close();
|
||||||
|
_socket.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -5,6 +5,10 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import path_provider_macos
|
||||||
|
import shared_preferences_macos
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
177
pubspec.lock
177
pubspec.lock
@ -85,6 +85,20 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "6.1.4"
|
||||||
|
file_picker:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: file_picker
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "5.2.1"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -104,11 +118,23 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
flutter_plugin_android_lifecycle:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_plugin_android_lifecycle
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.7"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_web_plugins:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
formz:
|
formz:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -158,6 +184,15 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
open_file:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
path: "."
|
||||||
|
ref: HEAD
|
||||||
|
resolved-ref: fbf68e4bb5cb3e262d8f8ebe10b2f8449ff8e030
|
||||||
|
url: "https://github.com/crazecoder/open_file"
|
||||||
|
source: git
|
||||||
|
version: "3.2.2"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -165,6 +200,76 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.8.2"
|
||||||
|
path_provider:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: path_provider
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.11"
|
||||||
|
path_provider_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_android
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.20"
|
||||||
|
path_provider_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_ios
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.11"
|
||||||
|
path_provider_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_linux
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.7"
|
||||||
|
path_provider_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_macos
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.6"
|
||||||
|
path_provider_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_platform_interface
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.5"
|
||||||
|
path_provider_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_windows
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.3"
|
||||||
|
platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: platform
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.0"
|
||||||
|
plugin_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: plugin_platform_interface
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.3"
|
||||||
|
process:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: process
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "4.2.4"
|
||||||
provider:
|
provider:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -172,6 +277,62 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.3"
|
version: "6.0.3"
|
||||||
|
shared_preferences:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: shared_preferences
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.15"
|
||||||
|
shared_preferences_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_android
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.13"
|
||||||
|
shared_preferences_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_ios
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
shared_preferences_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_linux
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
shared_preferences_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_macos
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.4"
|
||||||
|
shared_preferences_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_platform_interface
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
shared_preferences_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_web
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.4"
|
||||||
|
shared_preferences_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_windows
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -261,6 +422,20 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
|
xdg_directories:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xdg_directories
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0+2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.18.2 <3.0.0"
|
dart: ">=2.18.2 <3.0.0"
|
||||||
flutter: ">=1.16.0"
|
flutter: ">=3.0.0"
|
||||||
|
|||||||
@ -32,6 +32,7 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
|
path_provider: ^2.0.11
|
||||||
sqflite_common_ffi: ^2.1.1+1
|
sqflite_common_ffi: ^2.1.1+1
|
||||||
sqflite_common: ^2.3.0
|
sqflite_common: ^2.3.0
|
||||||
crypto: ^3.0.2
|
crypto: ^3.0.2
|
||||||
@ -40,6 +41,11 @@ dependencies:
|
|||||||
formz: ^0.4.1
|
formz: ^0.4.1
|
||||||
bloc: ^8.1.0
|
bloc: ^8.1.0
|
||||||
flutter_bloc: ^8.1.1
|
flutter_bloc: ^8.1.1
|
||||||
|
file_picker: ^5.2.1
|
||||||
|
open_file:
|
||||||
|
git:
|
||||||
|
url: https://github.com/crazecoder/open_file
|
||||||
|
shared_preferences: ^2.0.15
|
||||||
|
|
||||||
# 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.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user