You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Flutter+auto_route实现动态加载的类TikTok无限滚动信息流?

解决方案:自定义PageView结合AutoRoute实现无限滚动信息流

AutoTabsRouter.pageView的设计初衷是适配固定数量的标签页场景,无法直接支持动态懒加载的无限滚动需求。我们可以通过自定义PageView + 手动管理AutoRoute路由栈的方式实现TikTok式的垂直滑动信息流,同时保证路由状态同步。

核心思路

  1. 用PageView.builder实现垂直方向的无限懒加载,动态构建帖子页面
  2. 监听PageView的滑动事件,每次切换到新页面时,手动更新AutoRoute的路由栈
  3. 同步路由状态与PageView索引,确保回退、跳转等操作的一致性

具体实现步骤

1. 定义路由结构

首先在AutoRoute的路由配置中,定义首页和单个帖子页面:

@MaterialAutoRouter(
  routes: [
    AutoRoute(page: HomeFeedScreen, initial: true), // 信息流首页
    AutoRoute(page: PostDetailScreen), // 单个帖子页面
  ],
)
class $AppRouter {}

2. 实现首页的无限滚动PageView

在首页中使用PageView.builder构建垂直滑动列表,同时处理路由同步和懒加载逻辑:

class HomeFeedScreen extends StatefulWidget {
  const HomeFeedScreen({super.key});

  @override
  State<HomeFeedScreen> createState() => _HomeFeedScreenState();
}

class _HomeFeedScreenState extends State<HomeFeedScreen> {
  final PageController _pageController = PageController();
  final List<PostModel> _posts = [];
  int _currentPageIndex = 0;
  bool _isLoading = false;

  @override
  void initState() {
    super.initState();
    _loadInitialPosts();
    _pageController.addListener(_handlePageChange);
  }

  // 模拟加载初始帖子数据
  Future<void> _loadInitialPosts() async {
    setState(() => _isLoading = true);
    final fetchedPosts = await PostApi.fetchPosts(offset: 0);
    setState(() {
      _posts.addAll(fetchedPosts);
      _isLoading = false;
    });
  }

  // 加载更多帖子
  Future<void> _loadMorePosts() async {
    if (_isLoading) return;
    setState(() => _isLoading = true);
    final fetchedPosts = await PostApi.fetchPosts(offset: _posts.length);
    setState(() {
      _posts.addAll(fetchedPosts);
      _isLoading = false;
    });
  }

  // 处理页面切换,同步路由状态
  void _handlePageChange() {
    final newIndex = _pageController.page?.round() ?? 0;
    if (newIndex == _currentPageIndex) return;

    setState(() => _currentPageIndex = newIndex);
    final router = AutoRouter.of(context);

    // 清理路由栈中多余的帖子页面,避免栈过深
    while (router.stack.length > 1) {
      router.pop();
    }
    // 推送当前帖子的路由
    router.push(PostDetailRoute(postId: _posts[newIndex].id));

    // 滑动到接近列表末尾时触发懒加载
    if (newIndex >= _posts.length - 2) {
      _loadMorePosts();
    }
  }

  // 监听路由变化,同步PageView索引(比如从帖子页回退时)
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    final currentRoute = context.topRoute;
    if (currentRoute is PostDetailRoute) {
      final postId = currentRoute.postId;
      final index = _posts.indexWhere((p) => p.id == postId);
      if (index != -1 && index != _currentPageIndex) {
        _pageController.jumpToPage(index);
        setState(() => _currentPageIndex = index);
      }
    }
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PageView.builder(
        controller: _pageController,
        scrollDirection: Axis.vertical,
        itemCount: _posts.length + (_isLoading ? 1 : 0),
        itemBuilder: (context, index) {
          if (index == _posts.length) {
            // 加载更多的占位组件
            return const Center(child: CircularProgressIndicator());
          }
          final post = _posts[index];
          return PostDetailScreen(post: post);
        },
      ),
    );
  }
}

3. 实现单个帖子页面

class PostDetailScreen extends StatelessWidget {
  final PostModel post;
  const PostDetailScreen({super.key, required this.post});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // 帖子内容区域(视频/图片+文案等)
          Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text('帖子ID: ${post.id}'),
                const SizedBox(height: 16),
                Text(post.content),
              ],
            ),
          ),
          // 返回按钮
          Positioned(
            top: 40,
            left: 16,
            child: IconButton(
              icon: const Icon(Icons.arrow_back),
              onPressed: () => AutoRouter.of(context).pop(),
            ),
          ),
        ],
      ),
    );
  }
}

关键注意点

  • 路由栈优化:每次切换帖子时清理之前的帖子路由,避免路由栈无限增长,保证回退操作的合理性
  • 状态同步:通过didChangeDependencies监听路由变化,确保从其他页面返回时,PageView能自动跳转到对应的帖子位置
  • 懒加载时机:提前2个页面触发加载,避免用户滑动到空白区域等待
  • 数据状态管理:如果需要全局共享帖子数据,可结合Riverpod/Provider/Bloc等状态管理工具,简化页面间的数据传递

内容的提问来源于stack exchange,提问作者Martin Reindl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 02:35:00