Files
2026-09-15 15:44:13 +07:00

54 lines
1.9 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
/// 懒加载 + 保活的 IndexedStack(底部 tab 内容页标准写法)
///
/// 没进过的 tab 用空盒占位,首次切到才真正 build;进过一次就一直留在树里,
/// 切走只是不 paintstate / 滚动位置 / 播放器全部保留。
/// 相比 PageView:不需要 AutomaticKeepAlive 保活,也不会预建相邻页。
///
/// 非当前 tab 用 TickerMode(enabled: false) 关掉:IndexedStack 只是不 paint
/// 离屏页的动画/定时器照样在跑(跑马灯、Shimmer、轮播),看不见还空转。
class LazyIndexedStack extends StatefulWidget {
final int index;
final List<Widget> children;
const LazyIndexedStack({super.key, required this.index, required this.children});
@override
State<LazyIndexedStack> createState() => _LazyIndexedStackState();
}
class _LazyIndexedStackState extends State<LazyIndexedStack> {
//已建过的下标:进过一次就永久保活,不再退回占位
final _loaded = <int>{};
@override
void initState() {
super.initState();
_loaded.add(widget.index);
}
@override
void didUpdateWidget(covariant LazyIndexedStack oldWidget) {
super.didUpdateWidget(oldWidget);
_loaded.add(widget.index);
}
@override
Widget build(BuildContext context) {
return IndexedStack(
// 必须 expand:默认的 loose 会让子页拿到非 tight 约束,从而不再是 relayout boundary
// 离屏页一脏就把整个 stack 拖着重新布局(PageView 原来给的是 tight,每页各自隔离)。
// 顺带保证「根节点自身不撑满」的页也能满屏,不依赖各页自觉
sizing: StackFit.expand,
index: widget.index,
children: List.generate(
widget.children.length,
(i) => _loaded.contains(i)
? TickerMode(enabled: i == widget.index, child: widget.children[i])
: const SizedBox.shrink(),
),
);
}
}