假设你的社交应用需要展示:
传统实现方式:
ListView.builder(
itemCount: 10000,
itemBuilder: (context, index) {
return EmojiItem(
emoji: allEmojis[index], // 每个item独立实例
onTap: () => _selectEmoji(index),
);
},
)
问题爆发点:
核心思想: 运用共享技术有效支持大量细粒度对象。
三个关键角色:
class EmojiFlyweight {
final String code;
final ImageProvider image; // 共享的图片资源
const EmojiFlyweight(this.code, this.image);
}
// 享元工厂
class EmojiFactory {
static final Map<String, EmojiFlyweight> _cache = {};
static EmojiFlyweight getEmoji(String code) {
if (!_cache.containsKey(code)) {
final image = NetworkImage('https://emoji.cdn/$code.png');
_cache[code] = EmojiFlyweight(code, image);
}
return _cache[code]!;
}
}
class EmojiItem extends StatelessWidget {
final EmojiFlyweight emoji; // 内部状态(共享)
final bool isSelected; // 外部状态(不共享)
final VoidCallback onTap;
const EmojiItem({
required this.emoji,
required this.isSelected,
required this.onTap,
});
Widget build(BuildContext context) {
return GestureDetector(
onTap: onTap,
child: Container(
decoration: BoxDecoration(
border: isSelected
? Border.all(color: Colors.blue, width: 2)
: null,
),
child: Image(image: emoji.image),
),
);
}
}
ListView.builder(
itemCount: 10000,
itemBuilder: (context, index) {
final emojiCode = _getEmojiCode(index);
return EmojiItem(
emoji: EmojiFactory.getEmoji(emojiCode), // 共享对象
isSelected: _selectedIndex == index, // 外部状态
onTap: () => setState(() => _selectedIndex = index),
);
},
)
实现方式 | 内存占用 | 对象创建次数 |
---|---|---|
传统方式 | ~500MB | 10,000 |
享元模式 | ~5MB | 200(唯一表情数) |
class ThemeFlyweight {
final TextStyle textStyle;
final Color backgroundColor;
static final Map<String, ThemeFlyweight> _themes = {
'light': ThemeFlyweight(TextStyle(...), Colors.white),
'dark': ThemeFlyweight(TextStyle(...), Colors.black),
};
static ThemeFlyweight getTheme(String name) => _themes[name]!;
}
// 使用
Text(
'共享样式',
style: ThemeFlyweight.getTheme('light').textStyle,
)
class IconFlyweight {
final IconData iconData;
static final Map<String, IconFlyweight> _icons = {};
static IconFlyweight getIcon(String name) {
if (!_icons.containsKey(name)) {
final iconData = _loadIconData(name);
_icons[name] = IconFlyweight(iconData);
}
return _icons[name]!;
}
}
// 使用
Icon(IconFlyweight.getIcon('home').iconData)
class TextStyles {
static const TextStyle title = TextStyle(...);
static const TextStyle body = TextStyle(...);
static TextStyle getScaled(TextStyle base, double scale) {
return base.copyWith(fontSize: base.fontSize! * scale);
}
}
// 使用
Text('标题', style: TextStyles.title)
Text('内容', style: TextStyles.body)
ListView
/GridView
优化ListView.builder(
itemExtent: 56, // 固定高度提升性能
prototypeItem: EmojiItem(...), // 提供原型item
itemBuilder: (ctx, index) {
final emoji = _getSharedEmoji(index);
return EmojiItem(emoji: emoji);
},
)
Provider
结合class EmojiProvider extends ChangeNotifier {
final Map<String, EmojiFlyweight> _emojis = {};
EmojiFlyweight getEmoji(String code) {
if (!_emojis.containsKey(code)) {
_emojis[code] = EmojiFlyweight(code);
}
return _emojis[code]!;
}
}
// 使用
context.read<EmojiProvider>().getEmoji('smile')
class CachedImage extends StatelessWidget {
final String url;
const CachedImage(this.url);
Widget build(BuildContext context) {
return Image(
image: ResizeImage(
NetworkImage(url),
width: 100, // 限制缓存尺寸
),
cacheWidth: 100,
);
}
}
何时使用享元模式:
Flutter特化技巧:
// 使用const构造函数
class SharedWidget extends StatelessWidget {
const SharedWidget(); // const构造函数
}
// 在列表中使用const
ListView.builder(
itemBuilder: (ctx, i) => const SharedWidget(),
)
// 预加载享元对象
void preloadFlyweights() async {
await Future.wait([
EmojiFactory.getEmoji('smile'),
EmojiFactory.getEmoji('cry'),
]);
}
性能优化:
// 使用WeakReference避免内存泄漏
import 'package:weak_map/weak_map.dart';
final _weakCache = WeakMap<String, EmojiFlyweight>();
void cacheEmoji(String code, EmojiFlyweight emoji) {
_weakCache[code] = emoji;
}
测试策略:
test('享元工厂应返回相同实例', () {
final emoji1 = EmojiFactory.getEmoji('smile');
final emoji2 = EmojiFactory.getEmoji('smile');
expect(identical(emoji1, emoji2), isTrue);
});
特性 | 享元模式 | 单例模式 |
---|---|---|
目的 | 共享大量细粒度对象 | 确保全局唯一实例 |
实例数量 | 多个(但共享相同内在状态) | 严格一个 |
适用场景 | 列表项、样式、配置 | 全局服务、管理器 |
内存影响 | 显著减少内存占用 | 固定少量内存 |
class HierarchicalFlyweight {
static final Map<String, Map<String, dynamic>> _layers = {};
static T get<T>(String layer, String key, T Function() create) {
_layers[layer] ??= {};
_layers[layer]![key] ??= create();
return _layers[layer]![key] as T;
}
}
// 使用
final emoji = HierarchicalFlyweight.get('emojis', 'smile',
() => EmojiFlyweight('smile'));
class LruFlyweightCache {
final int maxSize;
final LinkedHashMap<String, EmojiFlyweight> _cache = LinkedHashMap();
EmojiFlyweight get(String code) {
if (_cache.containsKey(code)) {
final emoji = _cache.remove(code)!;
_cache[code] = emoji; // 移到最近使用
return emoji;
}
if (_cache.length >= maxSize) {
_cache.remove(_cache.keys.first); // 移除最久未使用
}
final emoji = EmojiFlyweight(code);
_cache[code] = emoji;
return emoji;
}
}
class ThemeManager {
static final Map<String, ThemeData> _themes = {};
static String _currentTheme = 'light';
static ThemeData get current => _themes[_currentTheme]!;
static void registerTheme(String name, ThemeData theme) {
_themes[name] = theme;
}
static void switchTheme(String name) {
_currentTheme = name;
}
}
// 使用
ThemeManager.registerTheme('dark', darkTheme);
Theme(theme: ThemeManager.current, child: ...)
总结:享元模式是你的内存优化师
设计启示: 当你的应用需要处理"千人一面"的场景时,享元模式就是你的"克隆魔法",让万级对象共享同一套DNA!