mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-03-25 00:14:42 +01:00
Fix face not focused in PeopleBrowser
This commit is contained in:
parent
f66345cded
commit
97d2513ded
7 changed files with 170 additions and 140 deletions
|
@ -1,25 +1,19 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:nc_photos/account.dart';
|
|
||||||
import 'package:nc_photos/theme.dart';
|
import 'package:nc_photos/theme.dart';
|
||||||
import 'package:nc_photos/widget/network_thumbnail.dart';
|
|
||||||
|
|
||||||
class CollectionListSmall extends StatelessWidget {
|
class CollectionListSmall extends StatelessWidget {
|
||||||
const CollectionListSmall({
|
const CollectionListSmall({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.account,
|
|
||||||
required this.label,
|
required this.label,
|
||||||
required this.coverUrl,
|
required this.child,
|
||||||
required this.fallbackBuilder,
|
|
||||||
this.onTap,
|
this.onTap,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget content = Stack(
|
Widget content = Stack(
|
||||||
children: [
|
children: [
|
||||||
SizedBox.expand(
|
SizedBox.expand(child: child),
|
||||||
child: _buildCoverImage(context),
|
|
||||||
),
|
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
|
@ -82,27 +76,7 @@ class CollectionListSmall extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCoverImage(BuildContext context) {
|
|
||||||
Widget buildPlaceholder() => Padding(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
child: fallbackBuilder(context),
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
return NetworkRectThumbnail(
|
|
||||||
account: account,
|
|
||||||
imageUrl: coverUrl!,
|
|
||||||
errorBuilder: (_) => buildPlaceholder(),
|
|
||||||
);
|
|
||||||
} catch (_) {
|
|
||||||
return FittedBox(
|
|
||||||
child: buildPlaceholder(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final Account account;
|
|
||||||
final String label;
|
final String label;
|
||||||
final String? coverUrl;
|
final Widget? child;
|
||||||
final Widget Function(BuildContext context) fallbackBuilder;
|
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,10 +15,10 @@ import 'package:nc_photos/exception_event.dart';
|
||||||
import 'package:nc_photos/exception_util.dart' as exception_util;
|
import 'package:nc_photos/exception_util.dart' as exception_util;
|
||||||
import 'package:nc_photos/k.dart' as k;
|
import 'package:nc_photos/k.dart' as k;
|
||||||
import 'package:nc_photos/snack_bar_manager.dart';
|
import 'package:nc_photos/snack_bar_manager.dart';
|
||||||
import 'package:nc_photos/theme.dart';
|
|
||||||
import 'package:nc_photos/widget/collection_browser.dart';
|
import 'package:nc_photos/widget/collection_browser.dart';
|
||||||
import 'package:nc_photos/widget/collection_list_item.dart';
|
import 'package:nc_photos/widget/collection_list_item.dart';
|
||||||
import 'package:nc_photos/widget/page_visibility_mixin.dart';
|
import 'package:nc_photos/widget/page_visibility_mixin.dart';
|
||||||
|
import 'package:nc_photos/widget/person_thumbnail.dart';
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
import 'package:to_string/to_string.dart';
|
import 'package:to_string/to_string.dart';
|
||||||
|
|
||||||
|
@ -186,14 +186,16 @@ class _ItemView extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return CollectionListSmall(
|
return CollectionListSmall(
|
||||||
account: account,
|
|
||||||
label: item.name,
|
label: item.name,
|
||||||
coverUrl: item.coverUrl,
|
|
||||||
fallbackBuilder: (context) => Icon(
|
|
||||||
Icons.person,
|
|
||||||
color: Theme.of(context).listPlaceholderForegroundColor,
|
|
||||||
),
|
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
|
child: LayoutBuilder(
|
||||||
|
builder: (context, constraints) => PersonThumbnail(
|
||||||
|
account: account,
|
||||||
|
coverUrl: item.coverUrl,
|
||||||
|
person: item.person,
|
||||||
|
dimension: constraints.maxWidth,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,11 @@ part of '../people_browser.dart';
|
||||||
class _Item {
|
class _Item {
|
||||||
_Item(this.person) {
|
_Item(this.person) {
|
||||||
try {
|
try {
|
||||||
_coverUrl = person.getCoverUrl(k.coverSize, k.coverSize);
|
_coverUrl = person.getCoverUrl(
|
||||||
|
k.photoLargeSize,
|
||||||
|
k.photoLargeSize,
|
||||||
|
isKeepAspectRatio: true,
|
||||||
|
);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
_log.warning("[_Item] Failed while getCoverUrl", e, stackTrace);
|
_log.warning("[_Item] Failed while getCoverUrl", e, stackTrace);
|
||||||
}
|
}
|
||||||
|
|
97
app/lib/widget/person_thumbnail.dart
Normal file
97
app/lib/widget/person_thumbnail.dart
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:nc_photos/account.dart';
|
||||||
|
import 'package:nc_photos/entity/person.dart';
|
||||||
|
import 'package:nc_photos/theme.dart';
|
||||||
|
import 'package:nc_photos/widget/network_thumbnail.dart';
|
||||||
|
|
||||||
|
class PersonThumbnail extends StatefulWidget {
|
||||||
|
const PersonThumbnail({
|
||||||
|
super.key,
|
||||||
|
required this.dimension,
|
||||||
|
required this.account,
|
||||||
|
required this.coverUrl,
|
||||||
|
required this.person,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<StatefulWidget> createState() => _PersonThumbnailState();
|
||||||
|
|
||||||
|
final double dimension;
|
||||||
|
final Account account;
|
||||||
|
final String? coverUrl;
|
||||||
|
final Person person;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PersonThumbnailState extends State<PersonThumbnail> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Widget content;
|
||||||
|
try {
|
||||||
|
var m = Matrix4.identity();
|
||||||
|
if (_layoutSize != null) {
|
||||||
|
final ratio = widget.dimension /
|
||||||
|
math.min(_layoutSize!.width, _layoutSize!.height);
|
||||||
|
final mm = widget.person.getCoverTransform(
|
||||||
|
widget.dimension.toInt(),
|
||||||
|
(_layoutSize!.width * ratio).toInt(),
|
||||||
|
(_layoutSize!.height * ratio).toInt(),
|
||||||
|
);
|
||||||
|
if (mm != null) {
|
||||||
|
m = mm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content = Transform(
|
||||||
|
transform: m,
|
||||||
|
child: NetworkRectThumbnail(
|
||||||
|
account: widget.account,
|
||||||
|
imageUrl: widget.coverUrl!,
|
||||||
|
errorBuilder: (_) => const _Placeholder(),
|
||||||
|
onSize: (size) {
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
setState(() {
|
||||||
|
_layoutSize = size;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (_layoutSize == null) {
|
||||||
|
content = Opacity(opacity: 0, child: content);
|
||||||
|
}
|
||||||
|
} catch (_) {
|
||||||
|
content = const FittedBox(
|
||||||
|
child: _Placeholder(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ClipRRect(
|
||||||
|
child: SizedBox.square(
|
||||||
|
dimension: widget.dimension,
|
||||||
|
child: Container(
|
||||||
|
color: Theme.of(context).listPlaceholderBackgroundColor,
|
||||||
|
constraints: const BoxConstraints.expand(),
|
||||||
|
child: content,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Size? _layoutSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Placeholder extends StatelessWidget {
|
||||||
|
const _Placeholder();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: Icon(
|
||||||
|
Icons.person,
|
||||||
|
color: Theme.of(context).listPlaceholderForegroundColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -243,14 +243,12 @@ class _PlaceItem {
|
||||||
});
|
});
|
||||||
|
|
||||||
Widget buildWidget(BuildContext context) => CollectionListSmall(
|
Widget buildWidget(BuildContext context) => CollectionListSmall(
|
||||||
account: account,
|
|
||||||
label: place,
|
label: place,
|
||||||
coverUrl: thumbUrl,
|
|
||||||
fallbackBuilder: (context) => Icon(
|
|
||||||
Icons.location_on,
|
|
||||||
color: Theme.of(context).listPlaceholderForegroundColor,
|
|
||||||
),
|
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
|
child: _PlaceThumbnail(
|
||||||
|
account: account,
|
||||||
|
coverUrl: thumbUrl,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final Account account;
|
final Account account;
|
||||||
|
@ -337,3 +335,43 @@ class _CountryItemView extends StatelessWidget {
|
||||||
final String text;
|
final String text;
|
||||||
final VoidCallback? onTap;
|
final VoidCallback? onTap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _PlaceThumbnail extends StatelessWidget {
|
||||||
|
const _PlaceThumbnail({
|
||||||
|
required this.account,
|
||||||
|
required this.coverUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
try {
|
||||||
|
return NetworkRectThumbnail(
|
||||||
|
account: account,
|
||||||
|
imageUrl: coverUrl!,
|
||||||
|
errorBuilder: (_) => const _Placeholder(),
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
return const FittedBox(
|
||||||
|
child: _Placeholder(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Account account;
|
||||||
|
final String? coverUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Placeholder extends StatelessWidget {
|
||||||
|
const _Placeholder();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(8),
|
||||||
|
child: Icon(
|
||||||
|
Icons.location_on,
|
||||||
|
color: Theme.of(context).listPlaceholderForegroundColor,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import 'dart:math' as math;
|
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
@ -23,6 +21,7 @@ import 'package:nc_photos/use_case/list_location_group.dart';
|
||||||
import 'package:nc_photos/widget/collection_browser.dart';
|
import 'package:nc_photos/widget/collection_browser.dart';
|
||||||
import 'package:nc_photos/widget/network_thumbnail.dart';
|
import 'package:nc_photos/widget/network_thumbnail.dart';
|
||||||
import 'package:nc_photos/widget/people_browser.dart';
|
import 'package:nc_photos/widget/people_browser.dart';
|
||||||
|
import 'package:nc_photos/widget/person_thumbnail.dart';
|
||||||
import 'package:nc_photos/widget/places_browser.dart';
|
import 'package:nc_photos/widget/places_browser.dart';
|
||||||
import 'package:nc_photos/widget/settings/account_settings.dart';
|
import 'package:nc_photos/widget/settings/account_settings.dart';
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
|
@ -342,11 +341,14 @@ class _LandingPersonWidget extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Center(
|
Center(
|
||||||
child: _PersonCoverImage(
|
child: ClipRRect(
|
||||||
dimension: 72,
|
borderRadius: BorderRadius.circular(72 / 2),
|
||||||
account: account,
|
child: PersonThumbnail(
|
||||||
person: person,
|
dimension: 72,
|
||||||
coverUrl: coverUrl,
|
account: account,
|
||||||
|
person: person,
|
||||||
|
coverUrl: coverUrl,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
|
|
@ -1,92 +1,5 @@
|
||||||
part of '../search_landing.dart';
|
part of '../search_landing.dart';
|
||||||
|
|
||||||
class _PersonCoverPlaceholder extends StatelessWidget {
|
|
||||||
const _PersonCoverPlaceholder();
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
child: Icon(
|
|
||||||
Icons.person,
|
|
||||||
color: Theme.of(context).listPlaceholderForegroundColor,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PersonCoverImage extends StatefulWidget {
|
|
||||||
const _PersonCoverImage({
|
|
||||||
required this.dimension,
|
|
||||||
required this.account,
|
|
||||||
required this.coverUrl,
|
|
||||||
required this.person,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<StatefulWidget> createState() => _PersonCoverImageState();
|
|
||||||
|
|
||||||
final double dimension;
|
|
||||||
final Account account;
|
|
||||||
final String? coverUrl;
|
|
||||||
final Person person;
|
|
||||||
}
|
|
||||||
|
|
||||||
class _PersonCoverImageState extends State<_PersonCoverImage> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
Widget cover;
|
|
||||||
try {
|
|
||||||
var m = Matrix4.identity();
|
|
||||||
if (_layoutSize != null) {
|
|
||||||
final ratio = widget.dimension /
|
|
||||||
math.min(_layoutSize!.width, _layoutSize!.height);
|
|
||||||
final mm = widget.person.getCoverTransform(
|
|
||||||
widget.dimension.toInt(),
|
|
||||||
(_layoutSize!.width * ratio).toInt(),
|
|
||||||
(_layoutSize!.height * ratio).toInt(),
|
|
||||||
);
|
|
||||||
if (mm != null) {
|
|
||||||
m = mm;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cover = Transform(
|
|
||||||
transform: m,
|
|
||||||
child: NetworkRectThumbnail(
|
|
||||||
account: widget.account,
|
|
||||||
imageUrl: widget.coverUrl!,
|
|
||||||
errorBuilder: (_) => const _PersonCoverPlaceholder(),
|
|
||||||
onSize: (size) {
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
setState(() {
|
|
||||||
_layoutSize = size;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} catch (_) {
|
|
||||||
cover = const FittedBox(
|
|
||||||
child: _PersonCoverPlaceholder(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SizedBox.square(
|
|
||||||
dimension: widget.dimension,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(widget.dimension / 2),
|
|
||||||
child: Container(
|
|
||||||
color: Theme.of(context).listPlaceholderBackgroundColor,
|
|
||||||
constraints: const BoxConstraints.expand(),
|
|
||||||
child: cover,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Size? _layoutSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
class _LocationCoverPlaceholder extends StatelessWidget {
|
class _LocationCoverPlaceholder extends StatelessWidget {
|
||||||
const _LocationCoverPlaceholder();
|
const _LocationCoverPlaceholder();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue