mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 09:08:40 +00:00
image preview bevor sending
This commit is contained in:
parent
50efa6530f
commit
b694b9d7fc
8 changed files with 261 additions and 60 deletions
|
|
@ -34,6 +34,7 @@
|
|||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility and
|
||||
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
"registerSubmitButton": "Register now!",
|
||||
"newMessageTitle": "New message",
|
||||
"chatsTitle": "Chats",
|
||||
"shareImagedEditorSendImage": "Send",
|
||||
"shareImagedEditorSaveImage": "Save",
|
||||
"shareImagedEditorSavedImage": "Saved",
|
||||
"searchUsernameInput": "Username",
|
||||
"searchUsernameTitle": "Search username",
|
||||
"searchUsernameNotFound": "Username not found",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
import 'package:gal/gal.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:twonly/main.dart';
|
||||
import 'package:twonly/src/model/contacts_model.dart';
|
||||
|
|
@ -25,6 +26,19 @@ Future<bool> isUserCreated() async {
|
|||
return true;
|
||||
}
|
||||
|
||||
Future<String?> saveImageToGallery(path) async {
|
||||
final hasAccess = await Gal.hasAccess();
|
||||
if (!hasAccess) {
|
||||
await Gal.requestAccess();
|
||||
}
|
||||
try {
|
||||
await Gal.putImage(path);
|
||||
return null;
|
||||
} on GalException catch (e) {
|
||||
return e.type.message;
|
||||
}
|
||||
}
|
||||
|
||||
Future<UserData?> getUser() async {
|
||||
final storage = getSecureStorage();
|
||||
String? userJson = await storage.read(key: "user_data");
|
||||
|
|
|
|||
|
|
@ -3,7 +3,37 @@ import 'dart:io';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:camerawesome/camerawesome_plugin.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:twonly/src/views/share_image_view.dart';
|
||||
import 'package:twonly/src/views/permissions_view.dart';
|
||||
import 'package:twonly/src/views/share_image_editor_view.dart';
|
||||
|
||||
class CameraPreviewViewPermission extends StatefulWidget {
|
||||
const CameraPreviewViewPermission({super.key});
|
||||
|
||||
@override
|
||||
State<CameraPreviewViewPermission> createState() =>
|
||||
_CameraPreviewViewPermission();
|
||||
}
|
||||
|
||||
class _CameraPreviewViewPermission extends State<CameraPreviewViewPermission> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: checkPermissions(),
|
||||
builder: (context, snap) {
|
||||
if (snap.hasData) {
|
||||
if (snap.data!) {
|
||||
return CameraPreviewView();
|
||||
} else {
|
||||
return PermissionHandlerView(onSuccess: () {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class CameraPreviewView extends StatefulWidget {
|
||||
const CameraPreviewView({super.key});
|
||||
|
|
@ -15,6 +45,7 @@ class CameraPreviewView extends StatefulWidget {
|
|||
class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||
double _lastZoom = 1;
|
||||
double _basePanY = 0;
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
|
@ -23,11 +54,16 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(top: 50, bottom: 30, left: 5, right: 5),
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 50, horizontal: 0),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(22),
|
||||
child: CameraAwesomeBuilder.custom(
|
||||
sensorConfig: SensorConfig.single(
|
||||
aspectRatio: CameraAspectRatios.ratio_16_9,
|
||||
zoom: 0.07,
|
||||
),
|
||||
previewFit: CameraPreviewFit.contain,
|
||||
progressIndicator: Container(),
|
||||
onMediaCaptureEvent: (event) {
|
||||
switch ((event.status, event.isPicture, event.isVideo)) {
|
||||
|
|
@ -40,9 +76,16 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
if (path == null) return;
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ShareImageView(image: path),
|
||||
),
|
||||
PageRouteBuilder(
|
||||
opaque: false,
|
||||
pageBuilder: (context, a1, a2) =>
|
||||
ShareImageEditorView(image: path),
|
||||
transitionsBuilder:
|
||||
(context, animation, secondaryAnimation, child) {
|
||||
return child;
|
||||
},
|
||||
transitionDuration: Duration.zero,
|
||||
reverseTransitionDuration: Duration.zero),
|
||||
);
|
||||
debugPrint('Picture saved: ${path}');
|
||||
},
|
||||
|
|
@ -93,6 +136,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
if (tmp != _lastZoom) {
|
||||
cameraState.sensorConfig.setZoom(tmp);
|
||||
setState(() {
|
||||
print(tmp);
|
||||
_lastZoom = tmp;
|
||||
});
|
||||
}
|
||||
|
|
@ -161,7 +205,6 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
// );
|
||||
},
|
||||
),
|
||||
previewPadding: const EdgeInsets.all(10),
|
||||
// onPreviewTapBuilder: (state) => OnPreviewTap(
|
||||
// onTap: (Offset position, PreviewSize flutterPreviewSize,
|
||||
// PreviewSize pixelPreviewSize) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import 'camera_preview_view.dart';
|
||||
import 'chat_list_view.dart';
|
||||
import 'permissions_view.dart';
|
||||
import 'profile_view.dart';
|
||||
import '../settings/settings_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -18,11 +17,6 @@ class HomeViewState extends State<HomeView> {
|
|||
final PageController _pageController = PageController(initialPage: 0);
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: checkPermissions(),
|
||||
builder: (context, snap) {
|
||||
if (snap.hasData) {
|
||||
if (snap.data!) {
|
||||
return Scaffold(
|
||||
body: PageView(
|
||||
controller: _pageController,
|
||||
|
|
@ -33,23 +27,22 @@ class HomeViewState extends State<HomeView> {
|
|||
},
|
||||
children: [
|
||||
ChatListView(),
|
||||
CameraPreviewView(),
|
||||
CameraPreviewViewPermission(),
|
||||
ProfileView(settingsController: widget.settingsController)
|
||||
],
|
||||
),
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
showSelectedLabels: false,
|
||||
showUnselectedLabels: false,
|
||||
selectedIconTheme: IconThemeData(
|
||||
color: const Color.fromARGB(255, 255, 255, 255)),
|
||||
selectedIconTheme:
|
||||
IconThemeData(color: const Color.fromARGB(255, 255, 255, 255)),
|
||||
items: [
|
||||
BottomNavigationBarItem(icon: Icon(Icons.chat), label: ""),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.camera_alt),
|
||||
label: "",
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(Icons.verified_user), label: ""),
|
||||
BottomNavigationBarItem(icon: Icon(Icons.verified_user), label: ""),
|
||||
],
|
||||
onTap: (int index) {
|
||||
setState(() {
|
||||
|
|
@ -62,13 +55,5 @@ class HomeViewState extends State<HomeView> {
|
|||
currentIndex: _activePageIdx,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return PermissionHandlerView(onSuccess: () {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
}
|
||||
return const CircularProgressIndicator();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
146
lib/src/views/share_image_editor_view.dart
Normal file
146
lib/src/views/share_image_editor_view.dart
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:twonly/src/utils.dart';
|
||||
import 'package:twonly/src/views/share_image_view.dart';
|
||||
|
||||
class ShareImageEditorView extends StatefulWidget {
|
||||
const ShareImageEditorView({super.key, required this.image});
|
||||
final String image;
|
||||
|
||||
@override
|
||||
State<ShareImageEditorView> createState() => _ShareImageEditorView();
|
||||
}
|
||||
|
||||
class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||
bool _isImageLoaded = false;
|
||||
bool _imageSaved = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
imageIsLoaded();
|
||||
}
|
||||
|
||||
Future imageIsLoaded() async {
|
||||
Future.delayed(Duration(milliseconds: 600), () {
|
||||
setState(() {
|
||||
_isImageLoaded = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: _isImageLoaded
|
||||
? Theme.of(context).colorScheme.surface
|
||||
: Colors.white.withAlpha(0),
|
||||
body: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Positioned(
|
||||
top: 0,
|
||||
// bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 50),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(22),
|
||||
child: Image.file(
|
||||
File(widget.image),
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
_isImageLoaded
|
||||
? Positioned(
|
||||
left: 10,
|
||||
top: 60,
|
||||
child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
size: 30,
|
||||
),
|
||||
color: Colors.white,
|
||||
onPressed: () async {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Container(),
|
||||
_isImageLoaded
|
||||
? Positioned(
|
||||
bottom: 70,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
OutlinedButton.icon(
|
||||
icon: _imageSaved
|
||||
? Icon(Icons.check)
|
||||
: Icon(Icons.save_rounded),
|
||||
style: OutlinedButton.styleFrom(
|
||||
iconColor: _imageSaved
|
||||
? Theme.of(context).colorScheme.outline
|
||||
: Theme.of(context).colorScheme.primary,
|
||||
foregroundColor: _imageSaved
|
||||
? Theme.of(context).colorScheme.outline
|
||||
: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
onPressed: () async {
|
||||
if (_imageSaved) return;
|
||||
final res = await saveImageToGallery(widget.image);
|
||||
if (res == null) {
|
||||
setState(() {
|
||||
_imageSaved = true;
|
||||
});
|
||||
}
|
||||
},
|
||||
label: Text(_imageSaved
|
||||
? AppLocalizations.of(context)!
|
||||
.shareImagedEditorSavedImage
|
||||
: AppLocalizations.of(context)!
|
||||
.shareImagedEditorSaveImage),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
FilledButton.icon(
|
||||
icon: Icon(Icons.send),
|
||||
onPressed: () async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
ShareImageView(image: widget.image)),
|
||||
);
|
||||
},
|
||||
style: ButtonStyle(
|
||||
padding: WidgetStateProperty.all<EdgeInsets>(
|
||||
EdgeInsets.symmetric(vertical: 10, horizontal: 30),
|
||||
),
|
||||
),
|
||||
label: Text(
|
||||
AppLocalizations.of(context)!
|
||||
.shareImagedEditorSendImage,
|
||||
style: TextStyle(fontSize: 17),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Container(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -450,6 +450,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
gal:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: gal
|
||||
sha256: "2771519c8b29f784d5e27f4efc2667667eef51c6c47cccaa0435a8fe8aa208e4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ dependencies:
|
|||
flutter_localizations:
|
||||
sdk: flutter
|
||||
flutter_secure_storage: ^9.2.2
|
||||
gal: ^2.3.1
|
||||
google_fonts: ^6.2.1
|
||||
image: ^4.3.0
|
||||
intl: any
|
||||
|
|
|
|||
Loading…
Reference in a new issue