♻️ Better image loading animation and more commonly used blurhash

This commit is contained in:
2026-01-02 18:32:37 +08:00
parent f1f5113b01
commit 78c1a284a5
44 changed files with 2043 additions and 2185 deletions

View File

@@ -24,193 +24,181 @@ class AccountNameplate extends HookConsumerWidget {
final user = ref.watch(accountProvider(name));
return Container(
decoration:
isOutlined
? BoxDecoration(
border: Border.all(
width: 1 / MediaQuery.of(context).devicePixelRatio,
color: Theme.of(context).dividerColor,
),
borderRadius: const BorderRadius.all(Radius.circular(8)),
)
: null,
decoration: isOutlined
? BoxDecoration(
border: Border.all(
width: 1 / MediaQuery.of(context).devicePixelRatio,
color: Theme.of(context).dividerColor,
),
borderRadius: const BorderRadius.all(Radius.circular(8)),
)
: null,
margin: padding,
child: Card(
margin: EdgeInsets.zero,
elevation: 0,
color: Colors.transparent,
child: user.when(
data:
(account) =>
account.profile.background != null
? AspectRatio(
aspectRatio: 16 / 9,
child: Stack(
children: [
// Background image
Positioned.fill(
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: CloudFileWidget(
item: account.profile.background!,
fit: BoxFit.cover,
),
),
data: (account) => account.profile.background != null
? AspectRatio(
aspectRatio: 16 / 9,
child: Stack(
children: [
// Background image
Positioned.fill(
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: CloudFileWidget(
item: account.profile.background!,
fit: BoxFit.cover,
),
),
),
// Gradient overlay for text readability
Positioned.fill(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Colors.black.withOpacity(0.8),
Colors.black.withOpacity(0.1),
Colors.transparent,
],
),
// Gradient overlay for text readability
Positioned.fill(
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Colors.black.withOpacity(0.8),
Colors.black.withOpacity(0.1),
Colors.transparent,
],
),
),
),
),
),
// Content positioned at the bottom
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: Row(
children: [
// Profile picture (equivalent to leading)
ProfilePictureWidget(
file: account.profile.picture,
),
),
// Content positioned at the bottom
Positioned(
left: 0,
right: 0,
bottom: 0,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: Row(
const SizedBox(width: 16),
// Text content (equivalent to title and subtitle)
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// Profile picture (equivalent to leading)
ProfilePictureWidget(
fileId: account.profile.picture?.id,
),
const SizedBox(width: 16),
// Text content (equivalent to title and subtitle)
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
AccountName(
account: account,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
Text(
'@${account.name}',
).textColor(Colors.white70),
],
AccountName(
account: account,
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
Text(
'@${account.name}',
).textColor(Colors.white70),
],
),
),
),
],
],
),
),
)
: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
decoration:
isOutlined
? BoxDecoration(
border: Border.all(
color:
Theme.of(context).colorScheme.outline,
),
borderRadius: BorderRadius.circular(12),
)
: null,
child: Row(
),
],
),
)
: Container(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
decoration: isOutlined
? BoxDecoration(
border: Border.all(
color: Theme.of(context).colorScheme.outline,
),
borderRadius: BorderRadius.circular(12),
)
: null,
child: Row(
children: [
// Profile picture (equivalent to leading)
ProfilePictureWidget(file: account.profile.picture),
const SizedBox(width: 16),
// Text content (equivalent to title and subtitle)
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// Profile picture (equivalent to leading)
ProfilePictureWidget(
fileId: account.profile.picture?.id,
),
const SizedBox(width: 16),
// Text content (equivalent to title and subtitle)
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
AccountName(
account: account,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
Text('@${account.name}'),
],
),
AccountName(
account: account,
style: TextStyle(fontWeight: FontWeight.bold),
),
Text('@${account.name}'),
],
),
),
loading:
() => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
],
),
),
child: Row(
children: [
// Loading indicator (equivalent to leading)
const CircularProgressIndicator(),
const SizedBox(width: 16),
// Loading text content (equivalent to title and subtitle)
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Text('loading').bold().tr(),
const SizedBox(height: 4),
const Text('...'),
],
),
),
],
loading: () => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: Row(
children: [
// Loading indicator (equivalent to leading)
const CircularProgressIndicator(),
const SizedBox(width: 16),
// Loading text content (equivalent to title and subtitle)
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Text('loading').bold().tr(),
const SizedBox(height: 4),
const Text('...'),
],
),
),
),
error:
(error, stackTrace) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
],
),
),
error: (error, stackTrace) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16.0,
vertical: 8.0,
),
child: Row(
children: [
// Error icon (equivalent to leading)
const Icon(Symbols.error),
const SizedBox(width: 16),
// Error text content (equivalent to title and subtitle)
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text('somethingWentWrong').tr().bold(),
const SizedBox(height: 4),
Text(error.toString()),
],
),
),
child: Row(
children: [
// Error icon (equivalent to leading)
const Icon(Symbols.error),
const SizedBox(width: 16),
// Error text content (equivalent to title and subtitle)
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text('somethingWentWrong').tr().bold(),
const SizedBox(height: 4),
Text(error.toString()),
],
),
),
],
),
),
],
),
),
),
),
);

View File

@@ -62,8 +62,8 @@ class AccountPickerSheet extends HookConsumerWidget {
),
),
autofocus: true,
onTapOutside:
(_) => FocusManager.instance.primaryFocus?.unfocus(),
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
),
),
Expanded(
@@ -74,23 +74,22 @@ class AccountPickerSheet extends HookConsumerWidget {
);
return searchResult.when(
data:
(accounts) => ListView.builder(
itemCount: accounts.length,
itemBuilder: (context, index) {
final account = accounts[index];
return ListTile(
leading: ProfilePictureWidget(
fileId: account.profile.picture?.id,
),
title: Text(account.nick),
subtitle: Text('@${account.name}'),
onTap: () => Navigator.of(context).pop(account),
);
},
),
loading:
() => const Center(child: CircularProgressIndicator()),
data: (accounts) => ListView.builder(
itemCount: accounts.length,
itemBuilder: (context, index) {
final account = accounts[index];
return ListTile(
leading: ProfilePictureWidget(
file: account.profile.picture,
),
title: Text(account.nick),
subtitle: Text('@${account.name}'),
onTap: () => Navigator.of(context).pop(account),
);
},
),
loading: () =>
const Center(child: CircularProgressIndicator()),
error: (error, stack) => Center(child: Text('Error: $error')),
);
},