修复白屏BUG、UI 边界溢出 BUG
This commit is contained in:
@@ -17,11 +17,34 @@ class ProfileNotifier extends AsyncNotifier<UserProfile> {
|
||||
Future<UserProfile> build() async {
|
||||
final isar = await ref.watch(isarProvider.future);
|
||||
final profile = await isar.userProfiles.get(AppDefaults.singletonId);
|
||||
if (profile != null) return profile;
|
||||
// Bootstrap should have created one; create defensively.
|
||||
final fresh = UserProfile.createDefault();
|
||||
await isar.writeTxn(() => isar.userProfiles.put(fresh));
|
||||
return fresh;
|
||||
if (profile == null) {
|
||||
// Bootstrap should have created one; create defensively.
|
||||
final fresh = UserProfile.createDefault();
|
||||
await isar.writeTxn(() => isar.userProfiles.put(fresh));
|
||||
return fresh;
|
||||
}
|
||||
if (_isLocalAvatarMissing(profile)) {
|
||||
return _clearMissingLocalAvatar(profile);
|
||||
}
|
||||
return profile;
|
||||
}
|
||||
|
||||
bool _isLocalAvatarMissing(UserProfile profile) {
|
||||
if (profile.avatarSource != AvatarSource.localFile) return false;
|
||||
final path = profile.avatarLocalPath?.trim();
|
||||
if (path == null || path.isEmpty) return true;
|
||||
return !File(path).existsSync();
|
||||
}
|
||||
|
||||
Future<UserProfile> _clearMissingLocalAvatar(UserProfile current) async {
|
||||
final updated = current.copyWith(
|
||||
avatarAssetPath: AppDefaults.defaultAvatarAssetPath,
|
||||
avatarLocalPath: null,
|
||||
avatarSource: AvatarSource.asset,
|
||||
);
|
||||
final isar = await ref.read(isarProvider.future);
|
||||
await isar.writeTxn(() => isar.userProfiles.put(updated));
|
||||
return updated;
|
||||
}
|
||||
|
||||
Future<void> updateNickname(String nickname) async {
|
||||
@@ -68,6 +91,19 @@ class ProfileNotifier extends AsyncNotifier<UserProfile> {
|
||||
Future<void> resetAvatar() async {
|
||||
final current = state.value;
|
||||
if (current == null) return;
|
||||
|
||||
final path = current.avatarLocalPath;
|
||||
if (path != null && path.isNotEmpty) {
|
||||
final file = File(path);
|
||||
if (await file.exists()) {
|
||||
try {
|
||||
await file.delete();
|
||||
} catch (_) {
|
||||
// Best-effort cleanup; profile reset still proceeds.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final updated = current.copyWith(
|
||||
avatarAssetPath: AppDefaults.defaultAvatarAssetPath,
|
||||
avatarLocalPath: null,
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:arcx/core/constants/app_defaults.dart';
|
||||
import 'package:arcx/data/local/models/models.dart';
|
||||
|
||||
/// Resolves a profile avatar to a local file when present, otherwise the
|
||||
/// default asset. Missing local files fall back safely (no FileImage crash).
|
||||
ImageProvider resolveAvatarImage(UserProfile? profile) {
|
||||
final path = profile?.avatarLocalPath?.trim();
|
||||
if (profile?.avatarSource == AvatarSource.localFile &&
|
||||
path != null &&
|
||||
path.isNotEmpty &&
|
||||
File(path).existsSync()) {
|
||||
return FileImage(File(path));
|
||||
}
|
||||
return const AssetImage(AppDefaults.defaultAvatarAssetPath);
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
|
||||
import 'package:arcx/app/app_theme.dart';
|
||||
import 'package:arcx/core/constants/app_defaults.dart';
|
||||
import 'package:arcx/data/local/models/models.dart';
|
||||
import 'package:arcx/features/profile/application/profile_controller.dart';
|
||||
import 'package:arcx/features/profile/presentation/avatar_image.dart';
|
||||
|
||||
class ProfileEditDialog extends ConsumerStatefulWidget {
|
||||
const ProfileEditDialog({super.key});
|
||||
@@ -137,23 +135,13 @@ class _AvatarPreview extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final p = profile;
|
||||
final ImageProvider image;
|
||||
if (p != null &&
|
||||
p.avatarSource == AvatarSource.localFile &&
|
||||
p.avatarLocalPath != null &&
|
||||
p.avatarLocalPath!.isNotEmpty) {
|
||||
image = FileImage(File(p.avatarLocalPath!));
|
||||
} else {
|
||||
image = const AssetImage(AppDefaults.defaultAvatarAssetPath);
|
||||
}
|
||||
return Stack(
|
||||
alignment: Alignment.bottomRight,
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 44,
|
||||
backgroundColor: AppTheme.surfaceMuted,
|
||||
backgroundImage: image,
|
||||
backgroundImage: resolveAvatarImage(profile),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
|
||||
Reference in New Issue
Block a user