mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 13:08:42 +00:00
new users and fix camera problem
This commit is contained in:
parent
e8e1061926
commit
931e9c0666
16 changed files with 214 additions and 591 deletions
|
|
@ -32,6 +32,8 @@
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
</application>
|
</application>
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||||
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
<!-- Required to query activities that can process text, see:
|
<!-- Required to query activities that can process text, see:
|
||||||
https://developer.android.com/training/package-visibility and
|
https://developer.android.com/training/package-visibility and
|
||||||
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,6 @@
|
||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||||
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:camera/camera.dart';
|
|
||||||
import 'package:twonly/src/providers/api_provider.dart';
|
import 'package:twonly/src/providers/api_provider.dart';
|
||||||
import 'package:twonly/src/providers/db_provider.dart';
|
import 'package:twonly/src/providers/db_provider.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
@ -30,8 +29,6 @@ void main() async {
|
||||||
'${record.level.name}: twonly:${record.loggerName}: ${record.message}');
|
'${record.level.name}: twonly:${record.loggerName}: ${record.message}');
|
||||||
});
|
});
|
||||||
|
|
||||||
var cameras = await availableCameras();
|
|
||||||
|
|
||||||
// Create or open the database
|
// Create or open the database
|
||||||
dbProvider = DbProvider();
|
dbProvider = DbProvider();
|
||||||
await dbProvider.ready;
|
await dbProvider.ready;
|
||||||
|
|
@ -54,5 +51,5 @@ void main() async {
|
||||||
// Run the app and pass in the SettingsController. The app listens to the
|
// Run the app and pass in the SettingsController. The app listens to the
|
||||||
// SettingsController for changes, then passes it further down to the
|
// SettingsController for changes, then passes it further down to the
|
||||||
// SettingsView.
|
// SettingsView.
|
||||||
runApp(MyApp(settingsController: settingsController, cameras: cameras));
|
runApp(MyApp(settingsController: settingsController));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:camera/camera.dart';
|
|
||||||
import 'package:twonly/main.dart';
|
import 'package:twonly/main.dart';
|
||||||
import 'views/home_view.dart';
|
import 'views/home_view.dart';
|
||||||
import 'views/register_view.dart';
|
import 'views/register_view.dart';
|
||||||
|
|
@ -11,11 +10,9 @@ import 'settings/settings_controller.dart';
|
||||||
|
|
||||||
/// The Widget that configures your application.
|
/// The Widget that configures your application.
|
||||||
class MyApp extends StatefulWidget {
|
class MyApp extends StatefulWidget {
|
||||||
const MyApp(
|
const MyApp({super.key, required this.settingsController});
|
||||||
{super.key, required this.settingsController, required this.cameras});
|
|
||||||
|
|
||||||
final SettingsController settingsController;
|
final SettingsController settingsController;
|
||||||
final List<CameraDescription> cameras;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MyApp> createState() => _MyAppState();
|
State<MyApp> createState() => _MyAppState();
|
||||||
|
|
@ -106,8 +103,7 @@ class _MyAppState extends State<MyApp> {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
return snapshot.data!
|
return snapshot.data!
|
||||||
? HomeView(
|
? HomeView(
|
||||||
settingsController: widget.settingsController,
|
settingsController: widget.settingsController)
|
||||||
cameras: widget.cameras)
|
|
||||||
: RegisterView(
|
: RegisterView(
|
||||||
callbackOnSuccess: () {
|
callbackOnSuccess: () {
|
||||||
_isUserCreated = isUserCreated();
|
_isUserCreated = isUserCreated();
|
||||||
|
|
@ -132,16 +128,6 @@ class _MyAppState extends State<MyApp> {
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: BorderRadius.all(
|
||||||
Radius.circular(10.0)), // Rounded top corners
|
Radius.circular(10.0)), // Rounded top corners
|
||||||
),
|
),
|
||||||
// child: Padding(
|
|
||||||
// padding: const EdgeInsets.all(
|
|
||||||
// 8.0), // Padding around the child
|
|
||||||
// child: Center(
|
|
||||||
// child: Text(
|
|
||||||
// 'Not Connected',
|
|
||||||
// style: TextStyle(fontSize: 24),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
50
lib/src/model/contacts_model.dart
Normal file
50
lib/src/model/contacts_model.dart
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:cv/cv.dart';
|
||||||
|
import 'package:twonly/main.dart';
|
||||||
|
|
||||||
|
class Contact {
|
||||||
|
Contact({required this.userId, required this.displayName});
|
||||||
|
final Uint8List userId;
|
||||||
|
final String displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DbContacts extends CvModelBase {
|
||||||
|
static const tableName = "contacts";
|
||||||
|
|
||||||
|
static const columnUserId = "contact_user_id";
|
||||||
|
final userId = CvField<Uint8List>(columnUserId);
|
||||||
|
|
||||||
|
static const columnDisplayName = "display_name";
|
||||||
|
final displayName = CvField<String>(columnDisplayName);
|
||||||
|
|
||||||
|
static const columnCreatedAt = "created_at";
|
||||||
|
final createdAt = CvField<DateTime>(columnCreatedAt);
|
||||||
|
|
||||||
|
static String getCreateTableString() {
|
||||||
|
return """
|
||||||
|
CREATE TABLE $tableName (
|
||||||
|
$columnUserId BINARY(16) NOT NULL PRIMARY KEY,
|
||||||
|
$columnDisplayName TEXT,
|
||||||
|
$columnCreatedAt DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
)
|
||||||
|
""";
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<List<Contact>> getUsers() async {
|
||||||
|
var users = await dbProvider.db!.query(tableName,
|
||||||
|
columns: [columnUserId, columnDisplayName, columnCreatedAt]);
|
||||||
|
if (users.isEmpty) return [];
|
||||||
|
|
||||||
|
List<Contact> parsedUsers = [];
|
||||||
|
for (int i = 0; i < users.length; i++) {
|
||||||
|
parsedUsers.add(Contact(
|
||||||
|
userId: users.cast()[i][columnUserId],
|
||||||
|
displayName: users.cast()[i][columnDisplayName]));
|
||||||
|
}
|
||||||
|
return parsedUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<CvField> get fields => [userId, createdAt];
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,6 @@ import 'package:twonly/src/proto/api/client_to_server.pb.dart' as client;
|
||||||
import 'package:twonly/src/proto/api/client_to_server.pbserver.dart';
|
import 'package:twonly/src/proto/api/client_to_server.pbserver.dart';
|
||||||
import 'package:twonly/src/proto/api/error.pb.dart';
|
import 'package:twonly/src/proto/api/error.pb.dart';
|
||||||
import 'package:twonly/src/proto/api/server_to_client.pb.dart' as server;
|
import 'package:twonly/src/proto/api/server_to_client.pb.dart' as server;
|
||||||
import 'package:twonly/src/proto/api/server_to_client.pbserver.dart';
|
|
||||||
import 'package:twonly/src/signal/signal_helper.dart';
|
import 'package:twonly/src/signal/signal_helper.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:twonly/src/utils.dart';
|
import 'package:twonly/src/utils.dart';
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:twonly/src/model/contacts_model.dart';
|
||||||
import 'package:twonly/src/model/identity_key_store_model.dart';
|
import 'package:twonly/src/model/identity_key_store_model.dart';
|
||||||
import 'package:twonly/src/model/model_constants.dart';
|
import 'package:twonly/src/model/model_constants.dart';
|
||||||
import 'package:twonly/src/model/pre_key_model.dart';
|
import 'package:twonly/src/model/pre_key_model.dart';
|
||||||
|
|
@ -67,6 +66,8 @@ class DbProvider {
|
||||||
await db
|
await db
|
||||||
.execute('DROP TABLE If EXISTS ${DbSignalIdentityKeyStore.tableName}');
|
.execute('DROP TABLE If EXISTS ${DbSignalIdentityKeyStore.tableName}');
|
||||||
await db.execute(DbSignalIdentityKeyStore.getCreateTableString());
|
await db.execute(DbSignalIdentityKeyStore.getCreateTableString());
|
||||||
|
await db.execute('DROP TABLE If EXISTS ${DbContacts.tableName}');
|
||||||
|
await db.execute(DbContacts.getCreateTableString());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future open() async {
|
Future open() async {
|
||||||
|
|
@ -74,7 +75,8 @@ class DbProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future remove() async {
|
Future remove() async {
|
||||||
await _createDb(db!);
|
await deleteDatabase(await fixPath(dbName));
|
||||||
|
// await _createDb(db!);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> fixPath(String path) async => path;
|
Future<String> fixPath(String path) async => path;
|
||||||
|
|
@ -82,8 +84,4 @@ class DbProvider {
|
||||||
Future close() async {
|
Future close() async {
|
||||||
await db!.close();
|
await db!.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Future deleteDb() async {
|
|
||||||
// await deleteDatabase(await fixPath(dbName));
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'dart:convert';
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
import 'package:twonly/src/model/signal_identity_json.dart';
|
import 'package:twonly/src/model/signal_identity_json.dart';
|
||||||
import 'package:twonly/src/proto/api/server_to_client.pb.dart';
|
import 'package:twonly/src/proto/api/server_to_client.pb.dart';
|
||||||
import 'package:twonly/src/utils.dart';
|
import 'package:twonly/src/utils.dart';
|
||||||
|
|
@ -170,9 +171,13 @@ class SignalHelper {
|
||||||
tempIdentityKey,
|
tempIdentityKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
await sessionBuilder.processPreKeyBundle(preKeyBundle);
|
await sessionBuilder.processPreKeyBundle(preKeyBundle);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
Logger("signal_helper").shout("Error: $e");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<ConnectSignalProtocolStore?> getSignalStore() async {
|
static Future<ConnectSignalProtocolStore?> getSignalStore() async {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,10 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'dart:ui';
|
|
||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:libsignal_protocol_dart/libsignal_protocol_dart.dart';
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:twonly/main.dart';
|
import 'package:twonly/main.dart';
|
||||||
|
import 'package:twonly/src/model/contacts_model.dart';
|
||||||
import 'package:twonly/src/signal/signal_helper.dart';
|
import 'package:twonly/src/signal/signal_helper.dart';
|
||||||
import 'package:twonly/src/providers/api_provider.dart';
|
import 'package:twonly/src/providers/api_provider.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
|
@ -88,18 +86,12 @@ Future<bool> addNewUser(String username) async {
|
||||||
print(res.value);
|
print(res.value);
|
||||||
print(res.value.userdata.userId);
|
print(res.value.userdata.userId);
|
||||||
|
|
||||||
await SignalHelper.addNewContact(res.value.userdata);
|
if (await SignalHelper.addNewContact(res.value.userdata)) {
|
||||||
|
await dbProvider.db!.insert(DbContacts.tableName, {
|
||||||
// final Map<String, dynamic> req = {};
|
DbContacts.columnDisplayName: username,
|
||||||
// req['identityKey'] =
|
DbContacts.columnUserId: res.value.userdata.userId
|
||||||
// (await signalStore.getIdentityKeyPair()).getPublicKey().serialize();
|
});
|
||||||
|
}
|
||||||
// req['signedPreKey'] = {
|
|
||||||
// 'id': signedPreKey.id,
|
|
||||||
// 'signature': signedPreKey.signature,
|
|
||||||
// 'key': signedPreKey.getKeyPair().publicKey.serialize(),
|
|
||||||
// };
|
|
||||||
|
|
||||||
print("Add new user: ${res}");
|
print("Add new user: ${res}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
// import 'package:camera/camera.dart';
|
|
||||||
// import 'camera_editor_view.dart';
|
|
||||||
// import 'package:flutter/gestures.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:camerawesome/camerawesome_plugin.dart';
|
|
||||||
|
|
||||||
class CameraPreviewView extends StatelessWidget {
|
|
||||||
const CameraPreviewView({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return const MaterialApp(
|
|
||||||
title: 'Custom CamerAwesome UI',
|
|
||||||
home: CameraPage(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CameraPage extends StatelessWidget {
|
|
||||||
const CameraPage({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
body: CameraAwesomeBuilder.awesome(
|
|
||||||
saveConfig: SaveConfig.photo(),
|
|
||||||
sensorConfig: SensorConfig.single(
|
|
||||||
sensor: Sensor.position(SensorPosition.back),
|
|
||||||
aspectRatio: CameraAspectRatios.ratio_1_1,
|
|
||||||
),
|
|
||||||
previewFit: CameraPreviewFit.contain,
|
|
||||||
previewPadding: const EdgeInsets.only(left: 150, top: 100),
|
|
||||||
previewAlignment: Alignment.topRight,
|
|
||||||
// Buttons of CamerAwesome UI will use this theme
|
|
||||||
theme: AwesomeTheme(
|
|
||||||
bottomActionsBackgroundColor: Colors.cyan.withAlpha(150),
|
|
||||||
buttonTheme: AwesomeButtonTheme(
|
|
||||||
backgroundColor: Colors.cyan.withAlpha(150),
|
|
||||||
iconSize: 20,
|
|
||||||
foregroundColor: Colors.white,
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
// Tap visual feedback (ripple, bounce...)
|
|
||||||
buttonBuilder: (child, onTap) {
|
|
||||||
return ClipOval(
|
|
||||||
child: Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
shape: const CircleBorder(),
|
|
||||||
child: InkWell(
|
|
||||||
splashColor: Colors.cyan,
|
|
||||||
highlightColor: Colors.cyan.withAlpha(150),
|
|
||||||
onTap: onTap,
|
|
||||||
child: child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
topActionsBuilder: (state) => AwesomeTopActions(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
state: state,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: AwesomeFilterWidget(
|
|
||||||
state: state,
|
|
||||||
filterListPosition: FilterListPosition.aboveButton,
|
|
||||||
filterListPadding: const EdgeInsets.only(top: 8),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
middleContentBuilder: (state) {
|
|
||||||
return Column(
|
|
||||||
children: [
|
|
||||||
const Spacer(),
|
|
||||||
Builder(builder: (context) {
|
|
||||||
return Container(
|
|
||||||
color: AwesomeThemeProvider.of(context)
|
|
||||||
.theme
|
|
||||||
.bottomActionsBackgroundColor,
|
|
||||||
child: const Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.only(bottom: 10, top: 10),
|
|
||||||
child: Text(
|
|
||||||
"Take your best shot!",
|
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontStyle: FontStyle.italic,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
bottomActionsBuilder: (state) => AwesomeBottomActions(
|
|
||||||
state: state,
|
|
||||||
left: AwesomeFlashButton(
|
|
||||||
state: state,
|
|
||||||
),
|
|
||||||
right: AwesomeCameraSwitchButton(
|
|
||||||
state: state,
|
|
||||||
scale: 1.0,
|
|
||||||
onSwitchTap: (state) {
|
|
||||||
state.switchCameraSensor(
|
|
||||||
aspectRatio: state.sensorConfig.aspectRatio,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,354 +1,117 @@
|
||||||
import 'package:camera/camera.dart';
|
// import 'package:camera/camera.dart';
|
||||||
|
// import 'camera_editor_view.dart';
|
||||||
|
// import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
// import 'package:camerawesome/camerawesome_plugin.dart';
|
import 'package:camerawesome/camerawesome_plugin.dart';
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
class CameraPreviewView extends StatefulWidget {
|
class CameraPreviewView extends StatelessWidget {
|
||||||
const CameraPreviewView({
|
const CameraPreviewView({super.key});
|
||||||
super.key,
|
|
||||||
required this.cameras,
|
@override
|
||||||
});
|
Widget build(BuildContext context) {
|
||||||
|
return const MaterialApp(
|
||||||
final List<CameraDescription> cameras;
|
title: 'Custom CamerAwesome UI',
|
||||||
static const routeName = '/camera_preview';
|
home: CameraPage(),
|
||||||
|
);
|
||||||
@override
|
}
|
||||||
CameraPreviewViewState createState() => CameraPreviewViewState();
|
}
|
||||||
}
|
|
||||||
|
class CameraPage extends StatelessWidget {
|
||||||
class CameraPreviewViewState extends State<CameraPreviewView> {
|
const CameraPage({super.key});
|
||||||
late CameraController _controller;
|
|
||||||
late Future<void> _initializeControllerFuture;
|
|
||||||
|
|
||||||
int _selectedCameraIdx = 0;
|
|
||||||
|
|
||||||
double _baseScaleFactor = 1.0;
|
|
||||||
double _basePanY = 0.0;
|
|
||||||
double _scaleFactor = 1;
|
|
||||||
double _minimumScaleFactor = 1;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
// To display the current output from the Camera,
|
|
||||||
// create a CameraController.
|
|
||||||
|
|
||||||
// _controller = CameraController(
|
|
||||||
// // Get a specific camera from the list of available cameras.
|
|
||||||
// widget.cameras.first,
|
|
||||||
// // Define the resolution to use.
|
|
||||||
// ResolutionPreset.max,
|
|
||||||
// );
|
|
||||||
|
|
||||||
// _controller.initialize().then((_) {
|
|
||||||
// _controller.value = _controller.value.copyWith(previewSize: size);
|
|
||||||
// setState(() {});
|
|
||||||
// });
|
|
||||||
|
|
||||||
// Next, initialize the controller. This returns a Future.
|
|
||||||
// _initializeControllerFuture = _controller.initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> updateController() async {
|
|
||||||
await _controller.setDescription(widget.cameras[_selectedCameraIdx]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
// Dispose of the controller when the widget is disposed.
|
|
||||||
// _controller.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
// function for cropping image
|
|
||||||
Future<void> takePicture() async {
|
|
||||||
try {
|
|
||||||
await _initializeControllerFuture;
|
|
||||||
// final image = await _controller.takePicture();
|
|
||||||
|
|
||||||
if (!context.mounted) return;
|
|
||||||
// await Navigator.of(context).push(
|
|
||||||
// MaterialPageRoute(
|
|
||||||
// builder: (context) => CameraEditorView(
|
|
||||||
// imagePath: image.path,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
} catch (e) {
|
|
||||||
// If an error occurs, log the error to the console.
|
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<double> getImageSize(double height, double width) {
|
|
||||||
// https://github.com/flutter/flutter/issues/15953#issuecomment-855182376
|
|
||||||
|
|
||||||
final screenH = max(height, width);
|
|
||||||
final screenW = min(height, width);
|
|
||||||
|
|
||||||
var ratioContainer = screenH / screenW;
|
|
||||||
|
|
||||||
var tmp = _controller.value.previewSize!;
|
|
||||||
final previewH = max(tmp.height, tmp.width);
|
|
||||||
final previewW = min(tmp.height, tmp.width);
|
|
||||||
|
|
||||||
var ratio = previewH / previewW;
|
|
||||||
var missing = ratioContainer - ratio;
|
|
||||||
|
|
||||||
final maxHeight = screenH;
|
|
||||||
final maxWidth = screenH * 0.5625;
|
|
||||||
|
|
||||||
_minimumScaleFactor = 1 + missing;
|
|
||||||
|
|
||||||
return [maxHeight, maxWidth];
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> updateScaleFactor(double newScale) async {
|
|
||||||
var minFactor = await _controller.getMinZoomLevel();
|
|
||||||
var maxFactor = await _controller.getMaxZoomLevel();
|
|
||||||
if (newScale < minFactor) {
|
|
||||||
newScale = minFactor;
|
|
||||||
}
|
|
||||||
if (newScale > maxFactor) {
|
|
||||||
newScale = maxFactor;
|
|
||||||
}
|
|
||||||
|
|
||||||
print(newScale);
|
|
||||||
if (newScale <= 1) {
|
|
||||||
await _controller.setZoomLevel(newScale);
|
|
||||||
} else {
|
|
||||||
//await _controller.setZoomLevel(1.0);
|
|
||||||
}
|
|
||||||
setState(() {
|
|
||||||
_scaleFactor = newScale;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container();
|
|
||||||
var isFront = widget.cameras[_selectedCameraIdx].lensDirection ==
|
|
||||||
CameraLensDirection.front;
|
|
||||||
// Fill this out in the next steps.
|
|
||||||
// You must wait until the controller is initialized before displaying the
|
|
||||||
// camera preview. Use a FutureBuilder to display a loading spinner until the
|
|
||||||
// controller has finished initializing.
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: GestureDetector(
|
body: CameraAwesomeBuilder.awesome(
|
||||||
onPanStart: (details) async {
|
saveConfig: SaveConfig.photo(),
|
||||||
if (_scaleFactor <= 1) {
|
sensorConfig: SensorConfig.single(
|
||||||
await updateScaleFactor(1);
|
sensor: Sensor.position(SensorPosition.back),
|
||||||
}
|
aspectRatio: CameraAspectRatios.ratio_1_1,
|
||||||
setState(() {
|
|
||||||
_basePanY = details.localPosition.dy;
|
|
||||||
_baseScaleFactor = _scaleFactor;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onPanUpdate: (details) async {
|
|
||||||
final diff = details.localPosition.dy - _basePanY;
|
|
||||||
final updateScale = diff / 50;
|
|
||||||
var tmp = _baseScaleFactor - updateScale;
|
|
||||||
if (tmp <= 1) tmp = 1.00001;
|
|
||||||
updateScaleFactor(tmp);
|
|
||||||
},
|
|
||||||
// onScaleStart: (details) {
|
|
||||||
// _baseScaleFactor = _scaleFactor;
|
|
||||||
// },
|
|
||||||
// onScaleUpdate: (details) async {
|
|
||||||
// // print(scale.scale);
|
|
||||||
// var scaleFactor =
|
|
||||||
// ((_baseScaleFactor * details.scale * 10).round() / 10);
|
|
||||||
// var maxFactor = await _controller.getMaxZoomLevel();
|
|
||||||
// if (scaleFactor > maxFactor) scaleFactor = maxFactor;
|
|
||||||
// if (scaleFactor != _scaleFactor) {
|
|
||||||
// await updateScaleFactor(scaleFactor);
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
onTap: () {},
|
|
||||||
onDoubleTap: () {
|
|
||||||
setState(() {
|
|
||||||
_selectedCameraIdx = (_selectedCameraIdx + 1) % 2;
|
|
||||||
updateController();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 60, bottom: 40),
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(22),
|
|
||||||
child: FutureBuilder<void>(
|
|
||||||
future: _initializeControllerFuture,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
if (snapshot.connectionState != ConnectionState.done) {
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
|
||||||
}
|
|
||||||
return Stack(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
children: [
|
|
||||||
LayoutBuilder(
|
|
||||||
builder:
|
|
||||||
(BuildContext context, BoxConstraints constraints) {
|
|
||||||
var height = constraints.maxHeight;
|
|
||||||
var width = constraints.maxWidth;
|
|
||||||
|
|
||||||
return OverflowBox(
|
|
||||||
maxHeight: getImageSize(height, width)[0],
|
|
||||||
maxWidth: getImageSize(height, width)[1],
|
|
||||||
child: Transform(
|
|
||||||
alignment: Alignment.center,
|
|
||||||
transform: Matrix4.rotationY(isFront ? pi : 0),
|
|
||||||
child: Transform.scale(
|
|
||||||
scale: _minimumScaleFactor +
|
|
||||||
(_scaleFactor > 1 ? _scaleFactor : 1) -
|
|
||||||
1,
|
|
||||||
child: CameraPreview(_controller)),
|
|
||||||
),
|
),
|
||||||
);
|
previewFit: CameraPreviewFit.contain,
|
||||||
},
|
previewPadding: const EdgeInsets.only(left: 150, top: 100),
|
||||||
),
|
previewAlignment: Alignment.topRight,
|
||||||
Positioned(
|
// Buttons of CamerAwesome UI will use this theme
|
||||||
bottom: 50,
|
theme: AwesomeTheme(
|
||||||
child: Column(
|
bottomActionsBackgroundColor: Colors.cyan.withAlpha(150),
|
||||||
children: [
|
buttonTheme: AwesomeButtonTheme(
|
||||||
CameraZoomButtons(
|
backgroundColor: Colors.cyan.withAlpha(150),
|
||||||
key: widget.key,
|
iconSize: 20,
|
||||||
isFront: isFront,
|
|
||||||
scaleFactor: _scaleFactor,
|
|
||||||
updateScaleFactor: updateScaleFactor,
|
|
||||||
controller: _controller,
|
|
||||||
),
|
|
||||||
SizedBox(height: 10),
|
|
||||||
GestureDetector(
|
|
||||||
onTap: () async {
|
|
||||||
await takePicture();
|
|
||||||
},
|
|
||||||
onLongPress: () async {},
|
|
||||||
child: Container(
|
|
||||||
height: 100,
|
|
||||||
width: 100,
|
|
||||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
|
||||||
padding: const EdgeInsets.all(2),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
border: Border.all(
|
|
||||||
width: 7,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CameraZoomButtons extends StatefulWidget {
|
|
||||||
const CameraZoomButtons(
|
|
||||||
{super.key,
|
|
||||||
required this.isFront,
|
|
||||||
required this.controller,
|
|
||||||
required this.updateScaleFactor,
|
|
||||||
required this.scaleFactor});
|
|
||||||
|
|
||||||
final bool isFront;
|
|
||||||
final CameraController controller;
|
|
||||||
final double scaleFactor;
|
|
||||||
final Function updateScaleFactor;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<CameraZoomButtons> createState() => _CameraZoomButtonsState();
|
|
||||||
}
|
|
||||||
|
|
||||||
String beautifulZoomScale(double scale) {
|
|
||||||
var tmp = scale.toStringAsFixed(1);
|
|
||||||
if (tmp[0] == "0") {
|
|
||||||
tmp = tmp.substring(1, tmp.length);
|
|
||||||
}
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CameraZoomButtonsState extends State<CameraZoomButtons> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final zoomButtonStyle = TextButton.styleFrom(
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
minimumSize: Size(40, 40),
|
padding: const EdgeInsets.all(16),
|
||||||
alignment: Alignment.center,
|
// Tap visual feedback (ripple, bounce...)
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap);
|
buttonBuilder: (child, onTap) {
|
||||||
|
return ClipOval(
|
||||||
final zoomTextStyle = TextStyle(fontSize: 13);
|
child: Material(
|
||||||
return ClipRRect(
|
color: Colors.transparent,
|
||||||
borderRadius: BorderRadius.circular(40.0),
|
shape: const CircleBorder(),
|
||||||
child: Container(
|
child: InkWell(
|
||||||
color: const Color.fromARGB(90, 0, 0, 0),
|
splashColor: Colors.cyan,
|
||||||
child: Row(
|
highlightColor: Colors.cyan.withAlpha(150),
|
||||||
children: widget.isFront
|
onTap: onTap,
|
||||||
? []
|
child: child,
|
||||||
: [
|
|
||||||
TextButton(
|
|
||||||
style: zoomButtonStyle,
|
|
||||||
onPressed: () async {
|
|
||||||
var level = await widget.controller.getMinZoomLevel();
|
|
||||||
widget.updateScaleFactor(level);
|
|
||||||
},
|
|
||||||
child: FutureBuilder(
|
|
||||||
future: widget.controller.getMinZoomLevel(),
|
|
||||||
builder: (context, snap) {
|
|
||||||
if (snap.hasData) {
|
|
||||||
var minLevel =
|
|
||||||
beautifulZoomScale(snap.data!.toDouble());
|
|
||||||
var currentLevel =
|
|
||||||
beautifulZoomScale(widget.scaleFactor);
|
|
||||||
return Text(
|
|
||||||
widget.scaleFactor < 1
|
|
||||||
? "${currentLevel}x"
|
|
||||||
: "${minLevel}x",
|
|
||||||
style: zoomTextStyle,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Text("");
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
TextButton(
|
),
|
||||||
style: zoomButtonStyle,
|
);
|
||||||
onPressed: () {
|
|
||||||
widget.updateScaleFactor(1.0);
|
|
||||||
},
|
},
|
||||||
child: Text(
|
),
|
||||||
widget.scaleFactor >= 1
|
),
|
||||||
? "${beautifulZoomScale(widget.scaleFactor)}x"
|
topActionsBuilder: (state) => AwesomeTopActions(
|
||||||
: "1.0x",
|
padding: EdgeInsets.zero,
|
||||||
style: zoomTextStyle,
|
state: state,
|
||||||
)),
|
children: [
|
||||||
TextButton(
|
Expanded(
|
||||||
style: zoomButtonStyle,
|
child: AwesomeFilterWidget(
|
||||||
onPressed: () async {
|
state: state,
|
||||||
var level = await widget.controller.getMaxZoomLevel();
|
filterListPosition: FilterListPosition.aboveButton,
|
||||||
widget.updateScaleFactor(level);
|
filterListPadding: const EdgeInsets.only(top: 8),
|
||||||
},
|
),
|
||||||
child: FutureBuilder(
|
),
|
||||||
future: widget.controller.getMaxZoomLevel(),
|
|
||||||
builder: (context, snap) {
|
|
||||||
if (snap.hasData) {
|
|
||||||
var maxLevel = snap.data?.toInt();
|
|
||||||
return Text("${maxLevel}x", style: zoomTextStyle);
|
|
||||||
} else {
|
|
||||||
return Text("");
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
middleContentBuilder: (state) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
const Spacer(),
|
||||||
|
Builder(builder: (context) {
|
||||||
|
return Container(
|
||||||
|
color: AwesomeThemeProvider.of(context)
|
||||||
|
.theme
|
||||||
|
.bottomActionsBackgroundColor,
|
||||||
|
child: const Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(bottom: 10, top: 10),
|
||||||
|
child: Text(
|
||||||
|
"Take your best shot!",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontStyle: FontStyle.italic,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
bottomActionsBuilder: (state) => AwesomeBottomActions(
|
||||||
|
state: state,
|
||||||
|
left: AwesomeFlashButton(
|
||||||
|
state: state,
|
||||||
|
),
|
||||||
|
right: AwesomeCameraSwitchButton(
|
||||||
|
state: state,
|
||||||
|
scale: 1.0,
|
||||||
|
onSwitchTap: (state) {
|
||||||
|
state.switchCameraSensor(
|
||||||
|
aspectRatio: state.sensorConfig.aspectRatio,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:camera/camera.dart';
|
|
||||||
import 'camera_preview_view.dart';
|
import 'camera_preview_view.dart';
|
||||||
import 'chat_list_view.dart';
|
import 'chat_list_view.dart';
|
||||||
import 'permissions_view.dart';
|
import 'permissions_view.dart';
|
||||||
|
|
@ -7,10 +6,8 @@ import '../settings/settings_controller.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class HomeView extends StatefulWidget {
|
class HomeView extends StatefulWidget {
|
||||||
const HomeView(
|
const HomeView({super.key, required this.settingsController});
|
||||||
{super.key, required this.settingsController, required this.cameras});
|
|
||||||
final SettingsController settingsController;
|
final SettingsController settingsController;
|
||||||
final List<CameraDescription> cameras;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<HomeView> createState() => HomeViewState();
|
State<HomeView> createState() => HomeViewState();
|
||||||
|
|
@ -36,7 +33,7 @@ class HomeViewState extends State<HomeView> {
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
ChatListView(),
|
ChatListView(),
|
||||||
CameraPreviewView(cameras: widget.cameras),
|
CameraPreviewView(),
|
||||||
ProfileView(settingsController: widget.settingsController)
|
ProfileView(settingsController: widget.settingsController)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
import 'package:twonly/src/model/contacts_model.dart';
|
||||||
import 'package:twonly/src/utils.dart';
|
import 'package:twonly/src/utils.dart';
|
||||||
|
import 'package:twonly/src/views/search_username_view.dart';
|
||||||
|
|
||||||
class NewMessageView extends StatefulWidget {
|
class NewMessageView extends StatefulWidget {
|
||||||
const NewMessageView({super.key});
|
const NewMessageView({super.key});
|
||||||
|
|
@ -10,49 +12,42 @@ class NewMessageView extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _NewMessageView extends State<NewMessageView> {
|
class _NewMessageView extends State<NewMessageView> {
|
||||||
final List<String> _known_users = [
|
List<Contact> _knownUsers = [];
|
||||||
"Alisa",
|
List<Contact> _filteredUsers = [];
|
||||||
"Klaus",
|
|
||||||
"John",
|
|
||||||
"Emma",
|
|
||||||
"Michael",
|
|
||||||
"Sophia",
|
|
||||||
"James",
|
|
||||||
"Olivia",
|
|
||||||
"Liam",
|
|
||||||
"Ava",
|
|
||||||
"Noah",
|
|
||||||
"Isabella",
|
|
||||||
"Lucas",
|
|
||||||
"Mia",
|
|
||||||
"Ethan",
|
|
||||||
"Charlotte",
|
|
||||||
];
|
|
||||||
List<String> _filteredUsers = [];
|
|
||||||
String _lastSearchQuery = '';
|
String _lastSearchQuery = '';
|
||||||
final TextEditingController searchUserName = TextEditingController();
|
final TextEditingController searchUserName = TextEditingController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
// Initialize the filtered users with all known users
|
_loadUsers();
|
||||||
_filteredUsers = List.from(_known_users);
|
}
|
||||||
|
|
||||||
|
Future<void> _loadUsers() async {
|
||||||
|
final users = await DbContacts.getUsers();
|
||||||
|
setState(() {
|
||||||
|
_knownUsers = users;
|
||||||
|
_filteredUsers = List.from(_knownUsers);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future _filterUsers(String query) async {
|
Future _filterUsers(String query) async {
|
||||||
if (query.isEmpty) {
|
if (query.isEmpty) {
|
||||||
_filteredUsers = List.from(_known_users);
|
_filteredUsers = List.from(_knownUsers);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (_lastSearchQuery.length < query.length) {
|
if (_lastSearchQuery.length < query.length) {
|
||||||
_filteredUsers = _known_users
|
_filteredUsers = _knownUsers
|
||||||
.where((user) => user.toLowerCase().contains(query.toLowerCase()))
|
.where((user) =>
|
||||||
|
user.displayName.toLowerCase().contains(query.toLowerCase()))
|
||||||
.toList();
|
.toList();
|
||||||
} else {
|
} else {
|
||||||
_filteredUsers = _filteredUsers
|
_filteredUsers = _filteredUsers
|
||||||
.where((user) => user.toLowerCase().contains(query.toLowerCase()))
|
.where((user) =>
|
||||||
|
user.displayName.toLowerCase().contains(query.toLowerCase()))
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
_lastSearchQuery = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -103,6 +98,11 @@ class _NewMessageView extends State<NewMessageView> {
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// Handle Nach Nutzername suchen button press
|
// Handle Nach Nutzername suchen button press
|
||||||
|
Navigator.pushReplacement(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => SearchUsernameView()),
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: Text('Nach Nutzername suchen'),
|
child: Text('Nach Nutzername suchen'),
|
||||||
),
|
),
|
||||||
|
|
@ -120,23 +120,22 @@ class _NewMessageView extends State<NewMessageView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserList extends StatelessWidget {
|
class UserList extends StatelessWidget {
|
||||||
final List<String> _known_users;
|
const UserList(this._knownUsers, {super.key});
|
||||||
|
final List<Contact> _knownUsers;
|
||||||
UserList(this._known_users);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// Step 1: Sort the users alphabetically
|
// Step 1: Sort the users alphabetically
|
||||||
_known_users.sort();
|
_knownUsers.sort((a, b) => a.displayName.compareTo(b.displayName));
|
||||||
|
|
||||||
// Step 2: Group users by their initials
|
// Step 2: Group users by their initials
|
||||||
Map<String, List<String>> groupedUsers = {};
|
Map<String, List<String>> groupedUsers = {};
|
||||||
for (var user in _known_users) {
|
for (var user in _knownUsers) {
|
||||||
String initial = user[0].toUpperCase();
|
String initial = user.displayName[0].toUpperCase();
|
||||||
if (!groupedUsers.containsKey(initial)) {
|
if (!groupedUsers.containsKey(initial)) {
|
||||||
groupedUsers[initial] = [];
|
groupedUsers[initial] = [];
|
||||||
}
|
}
|
||||||
groupedUsers[initial]!.add(user);
|
groupedUsers[initial]!.add(user.displayName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Create a list of sections
|
// Step 3: Create a list of sections
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ class PermissionHandlerViewState extends State<PermissionHandlerView> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Connect needs access to the camera and microphone for obvious reasons.",
|
"twonly needs access to the camera and microphone.",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
SizedBox(height: 50),
|
SizedBox(height: 50),
|
||||||
|
|
|
||||||
48
pubspec.lock
48
pubspec.lock
|
|
@ -126,46 +126,6 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.9.3"
|
version: "8.9.3"
|
||||||
camera:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: camera
|
|
||||||
sha256: "26ff41045772153f222ffffecba711a206f670f5834d40ebf5eed3811692f167"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.11.0+2"
|
|
||||||
camera_android_camerax:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: camera_android_camerax
|
|
||||||
sha256: "7cc6adf1868bdcf4e63a56b24b41692dfbad2bec1cdceea451c77798f6a605c3"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.6.13"
|
|
||||||
camera_avfoundation:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: camera_avfoundation
|
|
||||||
sha256: "3f81ee3e88a79b0b010f0233d42625926299551b05d5dc995267a0b35bc33247"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.9.18"
|
|
||||||
camera_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: camera_platform_interface
|
|
||||||
sha256: "953e7baed3a7c8fae92f7200afeb2be503ff1a17c3b4e4ed7b76f008c2810a31"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.9.0"
|
|
||||||
camera_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: camera_web
|
|
||||||
sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.3.5"
|
|
||||||
camerawesome:
|
camerawesome:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -368,14 +328,6 @@ packages:
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_plugin_android_lifecycle:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: flutter_plugin_android_lifecycle
|
|
||||||
sha256: "615a505aef59b151b46bbeef55b36ce2b6ed299d160c51d84281946f0aa0ce0e"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.24"
|
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ environment:
|
||||||
sdk: ^3.5.4
|
sdk: ^3.5.4
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
camera: ^0.11.0+2
|
|
||||||
camerawesome: ^2.1.0
|
camerawesome: ^2.1.0
|
||||||
collection: ^1.18.0
|
collection: ^1.18.0
|
||||||
cv: ^1.1.3
|
cv: ^1.1.3
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue