perf: fix remaining 11 performance issues across 5 screens
Critical — Image.network → CachedNetworkImage: - home_screen.dart: hero/carousel banner image now cached with placeholder - profile_screen.dart: avatar and event list tile images now cached - calendar_screen.dart: event card images now cached with placeholder High: - profile_screen.dart: TextEditingControllers in dialogs now properly disposed via .then() and after await to prevent memory leaks Medium: - search_screen.dart: shrinkWrap:true → ConstrainedBox(maxHeight:320) + ClampingScrollPhysics for smooth search result scrolling - learn_more_screen.dart: MediaQuery.of(context) cached once per method instead of being called multiple times on every frame
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/foundation.dart' show kIsWeb;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
@@ -273,6 +274,7 @@ class _ProfileScreenState extends State<ProfileScreen>
|
||||
final result = await showDialog<String?>(
|
||||
context: context,
|
||||
builder: (ctx) {
|
||||
// Note: ctl is disposed after dialog closes below
|
||||
final theme = Theme.of(ctx);
|
||||
return AlertDialog(
|
||||
title: const Text('Enter image path or URL'),
|
||||
@@ -305,6 +307,7 @@ class _ProfileScreenState extends State<ProfileScreen>
|
||||
},
|
||||
);
|
||||
|
||||
ctl.dispose();
|
||||
if (result == null || result.isEmpty) return;
|
||||
await _saveProfile(_username, _email, result);
|
||||
}
|
||||
@@ -318,6 +321,7 @@ class _ProfileScreenState extends State<ProfileScreen>
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (ctx) {
|
||||
// nameCtl and emailCtl are disposed via .then() below
|
||||
final theme = Theme.of(ctx);
|
||||
return DraggableScrollableSheet(
|
||||
expand: false,
|
||||
@@ -419,7 +423,10 @@ class _ProfileScreenState extends State<ProfileScreen>
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
).then((_) {
|
||||
nameCtl.dispose();
|
||||
emailCtl.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
// ───────── Avatar builder (reused, with size param) ─────────
|
||||
@@ -428,11 +435,14 @@ class _ProfileScreenState extends State<ProfileScreen>
|
||||
final path = _profileImage.trim();
|
||||
if (path.startsWith('http')) {
|
||||
return ClipOval(
|
||||
child: Image.network(path,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: path,
|
||||
width: size,
|
||||
height: size,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) =>
|
||||
placeholder: (_, __) =>
|
||||
Container(width: size, height: size, color: const Color(0xFFE5E7EB)),
|
||||
errorWidget: (_, __, ___) =>
|
||||
Icon(Icons.person, size: size / 2, color: Colors.grey)));
|
||||
}
|
||||
if (kIsWeb) {
|
||||
@@ -497,11 +507,16 @@ class _ProfileScreenState extends State<ProfileScreen>
|
||||
if (imageUrl.startsWith('http')) {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Image.network(imageUrl,
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: imageUrl,
|
||||
width: 60,
|
||||
height: 60,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => Container(
|
||||
placeholder: (_, __) => Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
color: const Color(0xFFE5E7EB)),
|
||||
errorWidget: (_, __, ___) => Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
color: theme.dividerColor,
|
||||
|
||||
Reference in New Issue
Block a user