✨ Share via image
This commit is contained in:
parent
6c32d76f78
commit
ad7a34ec18
@ -482,5 +482,7 @@
|
|||||||
"authPreferencesDesc": "Set the security behavior of your account",
|
"authPreferencesDesc": "Set the security behavior of your account",
|
||||||
"authMaximumAuthSteps": "Maximum authentication steps",
|
"authMaximumAuthSteps": "Maximum authentication steps",
|
||||||
"authMaximumAuthStepsDesc": "The maximum number of authentication steps when logging in, higher value is more secure, lower value is more convenient; default is 2",
|
"authMaximumAuthStepsDesc": "The maximum number of authentication steps when logging in, higher value is more secure, lower value is more convenient; default is 2",
|
||||||
"auditLog": "Audit log"
|
"auditLog": "Audit log",
|
||||||
|
"shareImage": "Share as image",
|
||||||
|
"shareImageFooter": "See more interesting posts on Solar Network"
|
||||||
}
|
}
|
||||||
|
@ -478,5 +478,7 @@
|
|||||||
"authPreferencesDesc": "调整账号的安全行为模式",
|
"authPreferencesDesc": "调整账号的安全行为模式",
|
||||||
"authMaximumAuthSteps": "最大认证步数",
|
"authMaximumAuthSteps": "最大认证步数",
|
||||||
"authMaximumAuthStepsDesc": "登陆时最多的验证步数,值越高则越安全,反之则会相对方便;默认设置为 2",
|
"authMaximumAuthStepsDesc": "登陆时最多的验证步数,值越高则越安全,反之则会相对方便;默认设置为 2",
|
||||||
"auditLog": "活动日志"
|
"auditLog": "活动日志",
|
||||||
|
"shareImage": "分享图片",
|
||||||
|
"shareImageFooter": "上 Solar Network 看更多有趣帖子"
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,7 @@ class AuditLogScreen extends StatefulWidget {
|
|||||||
class _AuditLogScreenState extends State<AuditLogScreen> {
|
class _AuditLogScreenState extends State<AuditLogScreen> {
|
||||||
bool _isBusy = true;
|
bool _isBusy = true;
|
||||||
|
|
||||||
int _totalEvent = 0;
|
final List<AuditEvent> _events = List.empty(growable: true);
|
||||||
List<AuditEvent> _events = List.empty(growable: true);
|
|
||||||
|
|
||||||
Future<void> _getEvents() async {
|
Future<void> _getEvents() async {
|
||||||
if (!_isBusy) setState(() => _isBusy = true);
|
if (!_isBusy) setState(() => _isBusy = true);
|
||||||
@ -38,7 +37,6 @@ class _AuditLogScreenState extends State<AuditLogScreen> {
|
|||||||
final result = PaginationResult.fromJson(resp.body);
|
final result = PaginationResult.fromJson(resp.body);
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_totalEvent = result.count;
|
|
||||||
_events.addAll(
|
_events.addAll(
|
||||||
result.data?.map((x) => AuditEvent.fromJson(x)).toList() ??
|
result.data?.map((x) => AuditEvent.fromJson(x)).toList() ??
|
||||||
List.empty(),
|
List.empty(),
|
||||||
|
@ -588,8 +588,6 @@ class _AccountProfilePageState extends State<AccountProfilePage> {
|
|||||||
color:
|
color:
|
||||||
Theme.of(context).colorScheme.surfaceContainerLow,
|
Theme.of(context).colorScheme.surfaceContainerLow,
|
||||||
child: PostListEntryWidget(
|
child: PostListEntryWidget(
|
||||||
backgroundColor:
|
|
||||||
Theme.of(context).colorScheme.surfaceContainerLow,
|
|
||||||
item: element,
|
item: element,
|
||||||
isClickable: true,
|
isClickable: true,
|
||||||
isNestedClickable: true,
|
isNestedClickable: true,
|
||||||
|
@ -393,9 +393,6 @@ class _DashboardScreenState extends State<DashboardScreen> {
|
|||||||
vertical: 8,
|
vertical: 8,
|
||||||
horizontal: 4,
|
horizontal: 4,
|
||||||
),
|
),
|
||||||
backgroundColor: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.surfaceContainerLow,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -160,13 +160,13 @@ class _ExploreScreenState extends State<ExploreScreen>
|
|||||||
toolbarHeight: AppTheme.toolbarHeight(context),
|
toolbarHeight: AppTheme.toolbarHeight(context),
|
||||||
leading: AppBarLeadingButton.adaptive(context),
|
leading: AppBarLeadingButton.adaptive(context),
|
||||||
actions: [
|
actions: [
|
||||||
|
const BackgroundStateWidget(),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.search),
|
icon: const Icon(Icons.search),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
AppRouter.instance.pushNamed('postSearch');
|
AppRouter.instance.pushNamed('postSearch');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const BackgroundStateWidget(),
|
|
||||||
const NotificationButton(),
|
const NotificationButton(),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: AppTheme.isLargeScreen(context) ? 8 : 16,
|
width: AppTheme.isLargeScreen(context) ? 8 : 16,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:firebase_analytics/firebase_analytics.dart';
|
import 'package:firebase_analytics/firebase_analytics.dart';
|
||||||
@ -5,6 +6,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
import 'package:screenshot/screenshot.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:solian/exts.dart';
|
import 'package:solian/exts.dart';
|
||||||
import 'package:solian/models/post.dart';
|
import 'package:solian/models/post.dart';
|
||||||
@ -12,6 +15,7 @@ import 'package:solian/platform.dart';
|
|||||||
import 'package:solian/providers/auth.dart';
|
import 'package:solian/providers/auth.dart';
|
||||||
import 'package:solian/router.dart';
|
import 'package:solian/router.dart';
|
||||||
import 'package:solian/screens/posts/post_editor.dart';
|
import 'package:solian/screens/posts/post_editor.dart';
|
||||||
|
import 'package:solian/widgets/posts/post_share.dart';
|
||||||
import 'package:solian/widgets/reports/abuse_report.dart';
|
import 'package:solian/widgets/reports/abuse_report.dart';
|
||||||
|
|
||||||
class PostAction extends StatefulWidget {
|
class PostAction extends StatefulWidget {
|
||||||
@ -84,6 +88,24 @@ class _PostActionState extends State<PostAction> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _shareImage() async {
|
||||||
|
final screenshot = ScreenshotController();
|
||||||
|
final image = await screenshot.captureFromWidget(
|
||||||
|
PostShareImage(item: widget.item),
|
||||||
|
context: context,
|
||||||
|
);
|
||||||
|
final directory = await getApplicationDocumentsDirectory();
|
||||||
|
final imageFile = await File(
|
||||||
|
'${directory.path}/temporary_share_image.png',
|
||||||
|
).create();
|
||||||
|
await imageFile.writeAsBytes(image);
|
||||||
|
|
||||||
|
final file = XFile(imageFile.path);
|
||||||
|
await Share.shareXFiles([file]);
|
||||||
|
|
||||||
|
await imageFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -135,16 +157,29 @@ class _PostActionState extends State<PostAction> {
|
|||||||
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
contentPadding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
leading: const Icon(Icons.share),
|
leading: const Icon(Icons.share),
|
||||||
title: Text('share'.tr),
|
title: Text('share'.tr),
|
||||||
trailing: PlatformInfo.isIOS || PlatformInfo.isAndroid
|
trailing: Row(
|
||||||
? IconButton(
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (PlatformInfo.isIOS || PlatformInfo.isAndroid)
|
||||||
|
IconButton(
|
||||||
icon: const Icon(Icons.link_off),
|
icon: const Icon(Icons.link_off),
|
||||||
tooltip: 'shareNoUri'.tr,
|
tooltip: 'shareNoUri'.tr,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await _doShare(noUri: true);
|
await _doShare(noUri: true);
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
)
|
),
|
||||||
: null,
|
if (PlatformInfo.isIOS || PlatformInfo.isAndroid)
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.image),
|
||||||
|
tooltip: 'shareImage'.tr,
|
||||||
|
onPressed: () async {
|
||||||
|
await _shareImage();
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
await _doShare();
|
await _doShare();
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
@ -41,7 +41,6 @@ class PostListWidget extends StatelessWidget {
|
|||||||
isClickable: isClickable,
|
isClickable: isClickable,
|
||||||
showFeaturedReply: true,
|
showFeaturedReply: true,
|
||||||
item: item,
|
item: item,
|
||||||
backgroundColor: backgroundColor,
|
|
||||||
onUpdate: () {
|
onUpdate: () {
|
||||||
controller.refresh();
|
controller.refresh();
|
||||||
},
|
},
|
||||||
@ -60,7 +59,6 @@ class PostListEntryWidget extends StatelessWidget {
|
|||||||
final bool isClickable;
|
final bool isClickable;
|
||||||
final bool showFeaturedReply;
|
final bool showFeaturedReply;
|
||||||
final Post item;
|
final Post item;
|
||||||
final Color? backgroundColor;
|
|
||||||
final EdgeInsets? padding;
|
final EdgeInsets? padding;
|
||||||
final Function onUpdate;
|
final Function onUpdate;
|
||||||
|
|
||||||
@ -71,7 +69,6 @@ class PostListEntryWidget extends StatelessWidget {
|
|||||||
required this.isClickable,
|
required this.isClickable,
|
||||||
required this.showFeaturedReply,
|
required this.showFeaturedReply,
|
||||||
required this.item,
|
required this.item,
|
||||||
this.backgroundColor,
|
|
||||||
this.padding,
|
this.padding,
|
||||||
required this.onUpdate,
|
required this.onUpdate,
|
||||||
});
|
});
|
||||||
|
92
lib/widgets/posts/post_share.dart
Normal file
92
lib/widgets/posts/post_share.dart
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
|
import 'package:solian/models/post.dart';
|
||||||
|
import 'package:solian/widgets/posts/post_item.dart';
|
||||||
|
import 'package:solian/widgets/root_container.dart';
|
||||||
|
|
||||||
|
class PostShareImage extends StatelessWidget {
|
||||||
|
final Post item;
|
||||||
|
|
||||||
|
const PostShareImage({super.key, required this.item});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final textColor = Theme.of(context).colorScheme.onSurface.withOpacity(0.3);
|
||||||
|
return RootContainer(
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Gap(8),
|
||||||
|
Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: Card(
|
||||||
|
child: PostItem(
|
||||||
|
item: item,
|
||||||
|
isShowEmbed: true,
|
||||||
|
isClickable: false,
|
||||||
|
showFeaturedReply: false,
|
||||||
|
isReactable: false,
|
||||||
|
isShowReply: false,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 8),
|
||||||
|
onComment: () {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: Image.asset(
|
||||||
|
'assets/logo.png',
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'shareImageFooter'.tr,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: textColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'Solsynth LLC © ${DateTime.now().year}',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 11,
|
||||||
|
color: textColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(8)),
|
||||||
|
child: Material(
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
child: QrImageView(
|
||||||
|
data: 'https://solsynth.dev/posts/${item.id}',
|
||||||
|
version: QrVersions.auto,
|
||||||
|
padding: const EdgeInsets.all(4),
|
||||||
|
size: 48,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingSymmetric(horizontal: 36, vertical: 24),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -25,7 +25,6 @@ class PostSingleDisplay extends StatelessWidget {
|
|||||||
isNestedClickable: true,
|
isNestedClickable: true,
|
||||||
showFeaturedReply: true,
|
showFeaturedReply: true,
|
||||||
onUpdate: onUpdate,
|
onUpdate: onUpdate,
|
||||||
backgroundColor: Theme.of(context).colorScheme.surfaceContainerLow,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
24
pubspec.lock
24
pubspec.lock
@ -1685,6 +1685,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
|
qr:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: qr
|
||||||
|
sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.2"
|
||||||
|
qr_flutter:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: qr_flutter
|
||||||
|
sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.0"
|
||||||
recase:
|
recase:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1757,6 +1773,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.3"
|
version: "0.1.3"
|
||||||
|
screenshot:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: screenshot
|
||||||
|
sha256: "63817697a7835e6ce82add4228e15d233b74d42975c143ad8cfe07009fab866b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
sdp_transform:
|
sdp_transform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -85,6 +85,8 @@ dependencies:
|
|||||||
syntax_highlight: ^0.4.0
|
syntax_highlight: ^0.4.0
|
||||||
flutter_udid: ^3.0.0
|
flutter_udid: ^3.0.0
|
||||||
timeline_tile: ^2.0.0
|
timeline_tile: ^2.0.0
|
||||||
|
screenshot: ^3.0.0
|
||||||
|
qr_flutter: ^4.1.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
Reference in New Issue
Block a user