✨ Detailed attachments
This commit is contained in:
parent
b53cb9fc81
commit
ab73916795
@ -338,11 +338,15 @@ class _PostEditorScreenState extends State<PostEditorScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (_isBusy)
|
if (_isBusy && _progress != null)
|
||||||
LinearProgressIndicator(
|
TweenAnimationBuilder<double>(
|
||||||
value: _progress,
|
tween: Tween(begin: 0, end: 1),
|
||||||
minHeight: 2,
|
duration: Duration(milliseconds: 300),
|
||||||
),
|
builder: (context, value, _) =>
|
||||||
|
LinearProgressIndicator(value: value, minHeight: 2),
|
||||||
|
)
|
||||||
|
else if (_isBusy)
|
||||||
|
const LinearProgressIndicator(value: null, minHeight: 2),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
|
37
lib/widgets/attachment/attachment_detail.dart
Normal file
37
lib/widgets/attachment/attachment_detail.dart
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import 'package:dismissible_page/dismissible_page.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:photo_view/photo_view.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:surface/providers/sn_network.dart';
|
||||||
|
import 'package:surface/types/attachment.dart';
|
||||||
|
import 'package:surface/widgets/universal_image.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
|
class AttachmentDetailPopup extends StatelessWidget {
|
||||||
|
final SnAttachment data;
|
||||||
|
final String? heroTag;
|
||||||
|
const AttachmentDetailPopup({super.key, required this.data, this.heroTag});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final sn = context.read<SnNetworkProvider>();
|
||||||
|
final uuid = Uuid();
|
||||||
|
|
||||||
|
return DismissiblePage(
|
||||||
|
onDismissed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
direction: DismissiblePageDismissDirection.down,
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
isFullScreen: true,
|
||||||
|
child: Hero(
|
||||||
|
tag: 'attachment-${data.rid}-${heroTag ?? uuid.v4()}',
|
||||||
|
child: PhotoView(
|
||||||
|
imageProvider: UniversalImage.provider(
|
||||||
|
sn.getAttachmentUrl(data.rid),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,55 @@
|
|||||||
|
import 'package:dismissible_page/dismissible_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:surface/providers/sn_network.dart';
|
import 'package:surface/providers/sn_network.dart';
|
||||||
import 'package:surface/types/attachment.dart';
|
import 'package:surface/types/attachment.dart';
|
||||||
|
import 'package:surface/widgets/attachment/attachment_detail.dart';
|
||||||
import 'package:surface/widgets/universal_image.dart';
|
import 'package:surface/widgets/universal_image.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class AttachmentItem extends StatelessWidget {
|
class AttachmentItem extends StatelessWidget {
|
||||||
final SnAttachment data;
|
final SnAttachment data;
|
||||||
const AttachmentItem({super.key, required this.data});
|
final bool isExpandable;
|
||||||
|
const AttachmentItem({
|
||||||
|
super.key,
|
||||||
|
required this.data,
|
||||||
|
this.isExpandable = false,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
Widget _buildContent(BuildContext context, String heroTag) {
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final tp = data.mimetype.split('/').firstOrNull;
|
final tp = data.mimetype.split('/').firstOrNull;
|
||||||
final sn = context.read<SnNetworkProvider>();
|
final sn = context.read<SnNetworkProvider>();
|
||||||
switch (tp) {
|
switch (tp) {
|
||||||
case 'image':
|
case 'image':
|
||||||
return UniversalImage(
|
return Hero(
|
||||||
sn.getAttachmentUrl(data.rid),
|
tag: 'attachment-${data.rid}-$heroTag',
|
||||||
fit: BoxFit.cover,
|
child: UniversalImage(
|
||||||
|
sn.getAttachmentUrl(data.rid),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
return const Placeholder();
|
return const Placeholder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final uuid = Uuid();
|
||||||
|
final heroTag = uuid.v4();
|
||||||
|
|
||||||
|
if (isExpandable) {
|
||||||
|
return GestureDetector(
|
||||||
|
child: _buildContent(context, heroTag),
|
||||||
|
onTap: () {
|
||||||
|
context.pushTransparentRoute(
|
||||||
|
AttachmentDetailPopup(data: data, heroTag: heroTag),
|
||||||
|
rootNavigator: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _buildContent(context, heroTag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
|
import 'package:responsive_framework/responsive_framework.dart';
|
||||||
import 'package:surface/types/attachment.dart';
|
import 'package:surface/types/attachment.dart';
|
||||||
import 'package:surface/widgets/attachment/attachment_item.dart';
|
import 'package:surface/widgets/attachment/attachment_item.dart';
|
||||||
|
|
||||||
@ -15,6 +18,10 @@ class AttachmentList extends StatelessWidget {
|
|||||||
this.maxListHeight,
|
this.maxListHeight,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static const double kMaxListItemWidth = 520;
|
||||||
|
static const BorderRadius kDefaultRadius =
|
||||||
|
BorderRadius.all(Radius.circular(8));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final borderSide = (bordered ?? false)
|
final borderSide = (bordered ?? false)
|
||||||
@ -23,11 +30,36 @@ class AttachmentList extends StatelessWidget {
|
|||||||
|
|
||||||
if (data.isEmpty) return const SizedBox.shrink();
|
if (data.isEmpty) return const SizedBox.shrink();
|
||||||
if (data.length == 1) {
|
if (data.length == 1) {
|
||||||
|
if (ResponsiveBreakpoints.of(context).largerThan(MOBILE)) {
|
||||||
|
return Container(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
maxWidth: math.min(
|
||||||
|
MediaQuery.of(context).size.width - 20,
|
||||||
|
kMaxListItemWidth,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border(top: borderSide, bottom: borderSide),
|
||||||
|
borderRadius: kDefaultRadius,
|
||||||
|
),
|
||||||
|
child: AspectRatio(
|
||||||
|
aspectRatio: data[0].metadata['ratio']?.toDouble() ?? 1,
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: kDefaultRadius,
|
||||||
|
child: AttachmentItem(data: data[0], isExpandable: true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(top: borderSide, bottom: borderSide),
|
border: Border(top: borderSide, bottom: borderSide),
|
||||||
),
|
),
|
||||||
child: AttachmentItem(data: data[0]),
|
child: AspectRatio(
|
||||||
|
aspectRatio: data[0].metadata['ratio']?.toDouble() ?? 1,
|
||||||
|
child: AttachmentItem(data: data[0], isExpandable: true),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,20 +71,22 @@ class AttachmentList extends StatelessWidget {
|
|||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: data.length,
|
itemCount: data.length,
|
||||||
itemBuilder: (context, idx) {
|
itemBuilder: (context, idx) {
|
||||||
const radius = BorderRadius.all(Radius.circular(8));
|
|
||||||
return Container(
|
return Container(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
maxWidth: MediaQuery.of(context).size.width - 20,
|
maxWidth: math.min(
|
||||||
|
MediaQuery.of(context).size.width - 20,
|
||||||
|
kMaxListItemWidth,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border(top: borderSide, bottom: borderSide),
|
border: Border(top: borderSide, bottom: borderSide),
|
||||||
borderRadius: radius,
|
borderRadius: kDefaultRadius,
|
||||||
),
|
),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: data[idx].metadata['ratio']?.toDouble() ?? 1,
|
aspectRatio: data[idx].metadata['ratio']?.toDouble() ?? 1,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: radius,
|
borderRadius: kDefaultRadius,
|
||||||
child: AttachmentItem(data: data[idx]),
|
child: AttachmentItem(data: data[idx], isExpandable: true),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
18
pubspec.lock
18
pubspec.lock
@ -334,6 +334,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.0"
|
||||||
|
dismissible_page:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: dismissible_page
|
||||||
|
sha256: "5b2316f770fe83583f770df1f6505cb19102081c5971979806e77f2e507a9958"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
dropdown_button2:
|
dropdown_button2:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -1034,6 +1042,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.2"
|
version: "6.0.2"
|
||||||
|
photo_view:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: photo_view
|
||||||
|
sha256: "1fc3d970a91295fbd1364296575f854c9863f225505c28c46e0a03e48960c75e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.0"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -1416,7 +1432,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: transitive
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: uuid
|
name: uuid
|
||||||
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
|
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
|
||||||
|
@ -67,6 +67,9 @@ dependencies:
|
|||||||
flutter_expandable_fab: ^2.3.0
|
flutter_expandable_fab: ^2.3.0
|
||||||
dropdown_button2: ^2.3.9
|
dropdown_button2: ^2.3.9
|
||||||
flutter_context_menu: ^0.2.0
|
flutter_context_menu: ^0.2.0
|
||||||
|
dismissible_page: ^1.0.2
|
||||||
|
uuid: ^4.5.1
|
||||||
|
photo_view: ^0.15.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
Reference in New Issue
Block a user