kopia lustrzana https://gitlab.com/mysocialportal/relatica
Add ImageControl that allows for doing on-demand loading and stub holder for all images
rodzic
ac6602dc45
commit
53e548336f
|
@ -1,9 +1,9 @@
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import '../../globals.dart';
|
import '../../globals.dart';
|
||||||
import '../../models/connection.dart';
|
import '../../models/connection.dart';
|
||||||
import '../../services/connections_manager.dart';
|
import '../../services/connections_manager.dart';
|
||||||
|
import '../image_control.dart';
|
||||||
|
|
||||||
class MentionAutocompleteOptions extends StatelessWidget {
|
class MentionAutocompleteOptions extends StatelessWidget {
|
||||||
const MentionAutocompleteOptions({
|
const MentionAutocompleteOptions({
|
||||||
|
@ -50,10 +50,12 @@ class MentionAutocompleteOptions extends StatelessWidget {
|
||||||
final user = users.elementAt(i);
|
final user = users.elementAt(i);
|
||||||
return ListTile(
|
return ListTile(
|
||||||
dense: true,
|
dense: true,
|
||||||
leading: CachedNetworkImage(
|
leading: ImageControl(
|
||||||
imageUrl: user.avatarUrl.toString(),
|
imageUrl: user.avatarUrl.toString(),
|
||||||
|
iconOverride: const Icon(Icons.person),
|
||||||
width: 25,
|
width: 25,
|
||||||
height: 25,
|
height: 25,
|
||||||
|
onTap: () => onMentionUserTap(user),
|
||||||
),
|
),
|
||||||
title: Text(user.name),
|
title: Text(user.name),
|
||||||
subtitle: Text('@${user.handle}'),
|
subtitle: Text('@${user.handle}'),
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
final _shownImageUrls = <String>{};
|
||||||
|
|
||||||
|
class ImageControl extends StatefulWidget {
|
||||||
|
final String imageUrl;
|
||||||
|
final double? width;
|
||||||
|
final double? height;
|
||||||
|
final Function()? onTap;
|
||||||
|
final Function()? onDoubleTap;
|
||||||
|
final Icon? iconOverride;
|
||||||
|
|
||||||
|
const ImageControl({
|
||||||
|
super.key,
|
||||||
|
required this.imageUrl,
|
||||||
|
this.iconOverride,
|
||||||
|
this.width,
|
||||||
|
this.height,
|
||||||
|
this.onTap,
|
||||||
|
this.onDoubleTap,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ImageControl> createState() => _ImageControlState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ImageControlState extends State<ImageControl> {
|
||||||
|
var shown = false;
|
||||||
|
|
||||||
|
void showImage() {
|
||||||
|
_shownImageUrls.add(widget.imageUrl);
|
||||||
|
setState(() {
|
||||||
|
shown = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
shown = _shownImageUrls.contains(widget.imageUrl);
|
||||||
|
final image = shown
|
||||||
|
? CachedNetworkImage(
|
||||||
|
imageUrl: widget.imageUrl,
|
||||||
|
width: widget.width,
|
||||||
|
height: widget.height,
|
||||||
|
)
|
||||||
|
: SizedBox(
|
||||||
|
width: widget.width,
|
||||||
|
height: widget.height,
|
||||||
|
child: Card(
|
||||||
|
color: Colors.black12,
|
||||||
|
shape: const RoundedRectangleBorder(),
|
||||||
|
child: widget.iconOverride ??
|
||||||
|
const Icon(
|
||||||
|
Icons.image,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: shown ? widget.onTap : showImage,
|
||||||
|
child: image,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
@ -12,6 +11,7 @@ import '../services/notifications_manager.dart';
|
||||||
import '../services/timeline_manager.dart';
|
import '../services/timeline_manager.dart';
|
||||||
import '../utils/dateutils.dart';
|
import '../utils/dateutils.dart';
|
||||||
import '../utils/snackbar_builder.dart';
|
import '../utils/snackbar_builder.dart';
|
||||||
|
import 'image_control.dart';
|
||||||
|
|
||||||
class NotificationControl extends StatelessWidget {
|
class NotificationControl extends StatelessWidget {
|
||||||
final UserNotification notification;
|
final UserNotification notification;
|
||||||
|
@ -43,26 +43,32 @@ class NotificationControl extends StatelessWidget {
|
||||||
final manager = context.watch<NotificationsManager>();
|
final manager = context.watch<NotificationsManager>();
|
||||||
final fromIcon =
|
final fromIcon =
|
||||||
getIt<ConnectionsManager>().getById(notification.fromId).fold(
|
getIt<ConnectionsManager>().getById(notification.fromId).fold(
|
||||||
onSuccess: (connection) => CachedNetworkImage(
|
onSuccess: (connection) => ImageControl(
|
||||||
imageUrl: connection.avatarUrl.toString(),
|
imageUrl: connection.avatarUrl.toString(),
|
||||||
width: iconSize,
|
iconOverride: const Icon(Icons.person),
|
||||||
height: iconSize),
|
|
||||||
onError: (error) => const SizedBox(
|
|
||||||
width: iconSize,
|
width: iconSize,
|
||||||
height: iconSize,
|
height: iconSize,
|
||||||
child: Icon(Icons.question_mark),
|
onTap: () async {
|
||||||
|
context.pushNamed(ScreenPaths.userProfile,
|
||||||
|
params: {'id': notification.fromId});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
onError: (error) => GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
context.pushNamed(ScreenPaths.userProfile,
|
||||||
|
params: {'id': notification.fromId});
|
||||||
|
},
|
||||||
|
child: const SizedBox(
|
||||||
|
width: iconSize,
|
||||||
|
height: iconSize,
|
||||||
|
child: Icon(Icons.person),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
tileColor: notification.dismissed ? null : Colors.black12,
|
tileColor: notification.dismissed ? null : Colors.black12,
|
||||||
leading: GestureDetector(
|
leading: fromIcon,
|
||||||
onTap: () async {
|
|
||||||
context.pushNamed(ScreenPaths.userProfile,
|
|
||||||
params: {'id': notification.fromId});
|
|
||||||
},
|
|
||||||
child: fromIcon,
|
|
||||||
),
|
|
||||||
title: GestureDetector(
|
title: GestureDetector(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
switch (notification.type) {
|
switch (notification.type) {
|
||||||
|
|
|
@ -1,21 +1,17 @@
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
import '../../globals.dart';
|
|
||||||
import '../../models/attachment_media_type_enum.dart';
|
import '../../models/attachment_media_type_enum.dart';
|
||||||
import '../../models/connection.dart';
|
|
||||||
import '../../models/entry_tree_item.dart';
|
import '../../models/entry_tree_item.dart';
|
||||||
import '../../models/timeline_entry.dart';
|
import '../../models/timeline_entry.dart';
|
||||||
import '../../screens/image_viewer_screen.dart';
|
import '../../screens/image_viewer_screen.dart';
|
||||||
import '../../services/connections_manager.dart';
|
|
||||||
import '../../services/timeline_manager.dart';
|
import '../../services/timeline_manager.dart';
|
||||||
import '../../utils/dateutils.dart';
|
|
||||||
import '../../utils/snackbar_builder.dart';
|
import '../../utils/snackbar_builder.dart';
|
||||||
import '../../utils/url_opening_utils.dart';
|
import '../../utils/url_opening_utils.dart';
|
||||||
|
import '../image_control.dart';
|
||||||
import '../padding.dart';
|
import '../padding.dart';
|
||||||
import 'interactions_bar_control.dart';
|
import 'interactions_bar_control.dart';
|
||||||
import 'status_header_control.dart';
|
import 'status_header_control.dart';
|
||||||
|
@ -127,51 +123,6 @@ class _StatusControlState extends State<StatusControl> {
|
||||||
: Card(color: Theme.of(context).splashColor, child: body);
|
: Card(color: Theme.of(context).splashColor, child: body);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildHeader(BuildContext context) {
|
|
||||||
final author = getIt<ConnectionsManager>()
|
|
||||||
.getById(entry.authorId)
|
|
||||||
.getValueOrElse(() => Connection());
|
|
||||||
return Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
CachedNetworkImage(
|
|
||||||
imageUrl: author.avatarUrl.toString(),
|
|
||||||
width: 32.0,
|
|
||||||
),
|
|
||||||
const HorizontalPadding(),
|
|
||||||
Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
author.name,
|
|
||||||
style: Theme.of(context).textTheme.bodyText1,
|
|
||||||
),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
ElapsedDateUtils.epochSecondsToString(
|
|
||||||
entry.backdatedTimestamp),
|
|
||||||
style: Theme.of(context).textTheme.caption,
|
|
||||||
),
|
|
||||||
const HorizontalPadding(),
|
|
||||||
Icon(
|
|
||||||
isPublic ? Icons.public : Icons.lock,
|
|
||||||
color: Theme.of(context).hintColor,
|
|
||||||
size: Theme.of(context).textTheme.caption?.fontSize,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
item.id,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildBody(BuildContext context) {
|
Widget buildBody(BuildContext context) {
|
||||||
return HtmlWidget(
|
return HtmlWidget(
|
||||||
entry.body,
|
entry.body,
|
||||||
|
@ -215,17 +166,15 @@ class _StatusControlState extends State<StatusControl> {
|
||||||
return Text('${item.explicitType}: ${item.uri}');
|
return Text('${item.explicitType}: ${item.uri}');
|
||||||
}
|
}
|
||||||
|
|
||||||
return InkWell(
|
return ImageControl(
|
||||||
|
width: 250.0,
|
||||||
|
height: 250.0,
|
||||||
|
imageUrl: item.thumbnailUri.toString(),
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
Navigator.push(context, MaterialPageRoute(builder: (context) {
|
Navigator.push(context, MaterialPageRoute(builder: (context) {
|
||||||
return ImageViewerScreen(attachment: item);
|
return ImageViewerScreen(attachment: item);
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
child: CachedNetworkImage(
|
|
||||||
width: 250.0,
|
|
||||||
height: 250.0,
|
|
||||||
imageUrl: item.thumbnailUri.toString(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
// return Text(item.toString());
|
// return Text(item.toString());
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
@ -9,6 +8,7 @@ import '../../routes.dart';
|
||||||
import '../../services/connections_manager.dart';
|
import '../../services/connections_manager.dart';
|
||||||
import '../../utils/dateutils.dart';
|
import '../../utils/dateutils.dart';
|
||||||
import '../../utils/url_opening_utils.dart';
|
import '../../utils/url_opening_utils.dart';
|
||||||
|
import '../image_control.dart';
|
||||||
import '../padding.dart';
|
import '../padding.dart';
|
||||||
|
|
||||||
class StatusHeaderControl extends StatelessWidget {
|
class StatusHeaderControl extends StatelessWidget {
|
||||||
|
@ -37,12 +37,11 @@ class StatusHeaderControl extends StatelessWidget {
|
||||||
.getValueOrElse(() => Connection());
|
.getValueOrElse(() => Connection());
|
||||||
return Wrap(
|
return Wrap(
|
||||||
children: [
|
children: [
|
||||||
GestureDetector(
|
ImageControl(
|
||||||
|
imageUrl: author.avatarUrl.toString(),
|
||||||
|
iconOverride: Icon(Icons.person),
|
||||||
|
width: 32.0,
|
||||||
onTap: () => goToProfile(context, author.id),
|
onTap: () => goToProfile(context, author.id),
|
||||||
child: CachedNetworkImage(
|
|
||||||
imageUrl: author.avatarUrl.toString(),
|
|
||||||
width: 32.0,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const HorizontalPadding(),
|
const HorizontalPadding(),
|
||||||
Column(
|
Column(
|
||||||
|
@ -81,12 +80,11 @@ class StatusHeaderControl extends StatelessWidget {
|
||||||
const HorizontalPadding(width: 3.0),
|
const HorizontalPadding(width: 3.0),
|
||||||
const Icon(Icons.repeat),
|
const Icon(Icons.repeat),
|
||||||
const HorizontalPadding(width: 3.0),
|
const HorizontalPadding(width: 3.0),
|
||||||
GestureDetector(
|
ImageControl(
|
||||||
|
imageUrl: reshareAuthor.avatarUrl.toString(),
|
||||||
|
iconOverride: Icon(Icons.person),
|
||||||
|
width: 32.0,
|
||||||
onTap: () => goToProfile(context, reshareAuthor.id),
|
onTap: () => goToProfile(context, reshareAuthor.id),
|
||||||
child: CachedNetworkImage(
|
|
||||||
imageUrl: reshareAuthor.avatarUrl.toString(),
|
|
||||||
width: 32.0,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const HorizontalPadding(width: 3.0),
|
const HorizontalPadding(width: 3.0),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
|
|
Ładowanie…
Reference in New Issue