Add ImageControl that allows for doing on-demand loading and stub holder for all images

codemagic-setup
Hank Grabowski 2022-12-30 10:55:05 -05:00
rodzic ac6602dc45
commit 53e548336f
5 zmienionych plików z 102 dodań i 83 usunięć

Wyświetl plik

@ -1,9 +1,9 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import '../../globals.dart';
import '../../models/connection.dart';
import '../../services/connections_manager.dart';
import '../image_control.dart';
class MentionAutocompleteOptions extends StatelessWidget {
const MentionAutocompleteOptions({
@ -50,10 +50,12 @@ class MentionAutocompleteOptions extends StatelessWidget {
final user = users.elementAt(i);
return ListTile(
dense: true,
leading: CachedNetworkImage(
leading: ImageControl(
imageUrl: user.avatarUrl.toString(),
iconOverride: const Icon(Icons.person),
width: 25,
height: 25,
onTap: () => onMentionUserTap(user),
),
title: Text(user.name),
subtitle: Text('@${user.handle}'),

Wyświetl plik

@ -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,
);
}
}

Wyświetl plik

@ -1,4 +1,3 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
import 'package:go_router/go_router.dart';
@ -12,6 +11,7 @@ import '../services/notifications_manager.dart';
import '../services/timeline_manager.dart';
import '../utils/dateutils.dart';
import '../utils/snackbar_builder.dart';
import 'image_control.dart';
class NotificationControl extends StatelessWidget {
final UserNotification notification;
@ -43,26 +43,32 @@ class NotificationControl extends StatelessWidget {
final manager = context.watch<NotificationsManager>();
final fromIcon =
getIt<ConnectionsManager>().getById(notification.fromId).fold(
onSuccess: (connection) => CachedNetworkImage(
imageUrl: connection.avatarUrl.toString(),
width: iconSize,
height: iconSize),
onError: (error) => const SizedBox(
onSuccess: (connection) => ImageControl(
imageUrl: connection.avatarUrl.toString(),
iconOverride: const Icon(Icons.person),
width: 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(
tileColor: notification.dismissed ? null : Colors.black12,
leading: GestureDetector(
onTap: () async {
context.pushNamed(ScreenPaths.userProfile,
params: {'id': notification.fromId});
},
child: fromIcon,
),
leading: fromIcon,
title: GestureDetector(
onTap: () async {
switch (notification.type) {

Wyświetl plik

@ -1,21 +1,17 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
import 'package:logging/logging.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../globals.dart';
import '../../models/attachment_media_type_enum.dart';
import '../../models/connection.dart';
import '../../models/entry_tree_item.dart';
import '../../models/timeline_entry.dart';
import '../../screens/image_viewer_screen.dart';
import '../../services/connections_manager.dart';
import '../../services/timeline_manager.dart';
import '../../utils/dateutils.dart';
import '../../utils/snackbar_builder.dart';
import '../../utils/url_opening_utils.dart';
import '../image_control.dart';
import '../padding.dart';
import 'interactions_bar_control.dart';
import 'status_header_control.dart';
@ -127,51 +123,6 @@ class _StatusControlState extends State<StatusControl> {
: 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) {
return HtmlWidget(
entry.body,
@ -215,17 +166,15 @@ class _StatusControlState extends State<StatusControl> {
return Text('${item.explicitType}: ${item.uri}');
}
return InkWell(
return ImageControl(
width: 250.0,
height: 250.0,
imageUrl: item.thumbnailUri.toString(),
onTap: () async {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ImageViewerScreen(attachment: item);
}));
},
child: CachedNetworkImage(
width: 250.0,
height: 250.0,
imageUrl: item.thumbnailUri.toString(),
),
);
// return Text(item.toString());
},

Wyświetl plik

@ -1,4 +1,3 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
@ -9,6 +8,7 @@ import '../../routes.dart';
import '../../services/connections_manager.dart';
import '../../utils/dateutils.dart';
import '../../utils/url_opening_utils.dart';
import '../image_control.dart';
import '../padding.dart';
class StatusHeaderControl extends StatelessWidget {
@ -37,12 +37,11 @@ class StatusHeaderControl extends StatelessWidget {
.getValueOrElse(() => Connection());
return Wrap(
children: [
GestureDetector(
ImageControl(
imageUrl: author.avatarUrl.toString(),
iconOverride: Icon(Icons.person),
width: 32.0,
onTap: () => goToProfile(context, author.id),
child: CachedNetworkImage(
imageUrl: author.avatarUrl.toString(),
width: 32.0,
),
),
const HorizontalPadding(),
Column(
@ -81,12 +80,11 @@ class StatusHeaderControl extends StatelessWidget {
const HorizontalPadding(width: 3.0),
const Icon(Icons.repeat),
const HorizontalPadding(width: 3.0),
GestureDetector(
ImageControl(
imageUrl: reshareAuthor.avatarUrl.toString(),
iconOverride: Icon(Icons.person),
width: 32.0,
onTap: () => goToProfile(context, reshareAuthor.id),
child: CachedNetworkImage(
imageUrl: reshareAuthor.avatarUrl.toString(),
width: 32.0,
),
),
const HorizontalPadding(width: 3.0),
GestureDetector(