Fix face not focused in PeopleBrowser

This commit is contained in:
Ming Ming 2023-07-23 02:57:01 +08:00
parent f66345cded
commit 97d2513ded
7 changed files with 170 additions and 140 deletions

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

@ -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();