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

Flutter中TabViewBar内动态列表无法滚动问题求助

问题分析

你这问题核心是嵌套了太多不必要的滚动组件,外层SingleChildScrollView套ListView,里面又套SingleChildScrollView和ListView,滚动事件全被外层容器拦截了,加上第二个标签页的列表还加了NeverScrollableScrollPhysics,直接锁死滚动,自然动不了。

解决方案

简化布局层级,去掉多余的滚动容器,让标签页内的列表拥有独立滚动能力:

import 'package:flutter/material.dart';
import 'package:autoscale_tabbarview/autoscale_tabbarview.dart';

class RecipeDetailPage extends StatefulWidget {
  final String recipeName;
  final String estTimeMin;
  final List availableIngredients;
  final List missingIngredients;
  final Map<String, dynamic> instructions;
  final String thumbnailUrl;

  const RecipeDetailPage({
    super.key,
    required this.recipeName,
    required this.estTimeMin,
    required this.availableIngredients,
    required this.missingIngredients,
    required this.instructions,
    required this.thumbnailUrl,
  });

  @override
  RecipeDetailPageState createState() => RecipeDetailPageState();
}

class RecipeDetailPageState extends State<RecipeDetailPage>
    with SingleTickerProviderStateMixin {
  late TabController _controller;

  @override
  void initState() {
    super.initState();
    _controller = TabController(length: 2, vsync: this);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.recipeName),
      ),
      body: Column(
        children: [
          // 顶部固定内容:图片、耗时
          Image.network(widget.thumbnailUrl),
          const SizedBox(height: 10),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 10),
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                const Icon(
                  Icons.timer,
                  size: 15,
                  color: Colors.blueGrey,
                ),
                const SizedBox(width: 8),
                Text(
                  "${widget.estTimeMin} mins",
                  style: const TextStyle(
                    fontSize: 15,
                    fontWeight: FontWeight.bold,
                    color: Colors.blueGrey,
                  ),
                ),
              ],
            ),
          ),
          const SizedBox(height: 10),
          // TabBar
          Align(
            alignment: Alignment.centerLeft,
            child: TabBar(
              controller: _controller,
              tabs: const [
                Tab(
                  child: Text(
                    'Ingredients',
                    style: TextStyle(
                      fontSize: 15,
                      fontWeight: FontWeight.bold,
                      color: Colors.black,
                    ),
                  ),
                ),
                Tab(
                  child: Text(
                    'Instructions',
                    style: TextStyle(
                      fontSize: 15,
                      fontWeight: FontWeight.bold,
                      color: Colors.black,
                    ),
                  ),
                ),
              ],
            ),
          ),
          // 标签页内容区域,占满剩余空间
          Expanded(
            child: AutoScaleTabBarView(
              controller: _controller,
              children: <Widget>[
                // 食材列表:去掉shrinkWrap,让列表自适应滚动
                ListView(
                  padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
                  children: [
                    ...widget.availableIngredients.map(
                      (ingredient) => ListTile(
                        leading: const Icon(
                          Icons.check_circle,
                          color: Colors.green,
                          size: 18,
                        ),
                        title: Text(
                          ingredient,
                          style: const TextStyle(fontSize: 18),
                        ),
                        visualDensity: const VisualDensity(horizontal: 0, vertical: -4),
                      ),
                    ),
                    ...widget.missingIngredients.map(
                      (ingredient) => ListTile(
                        leading: const Icon(
                          Icons.question_mark,
                          color: Colors.blue,
                          size: 18,
                        ),
                        title: Text(
                          ingredient,
                          style: const TextStyle(fontSize: 18),
                        ),
                        visualDensity: const VisualDensity(horizontal: 0, vertical: -4),
                      ),
                    ),
                  ],
                ),
                // 步骤列表:移除NeverScrollablePhysics,恢复滚动
                ListView.separated(
                  padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
                  itemCount: widget.instructions.length,
                  separatorBuilder: (BuildContext context, int index) => Divider(
                    color: Colors.blueGrey.withOpacity(0.5),
                    indent: 25,
                    endIndent: 25,
                    thickness: 2,
                  ),
                  itemBuilder: (BuildContext context, int index) {
                    return ListTile(
                      leading: CircleAvatar(
                        backgroundColor: Colors.lightBlue,
                        radius: 15,
                        child: Text(
                          (index + 1).toString(),
                          style: const TextStyle(fontSize: 15, color: Colors.white),
                        ),
                      ),
                      title: Text(
                        widget.instructions['step${index + 1}'],
                        style: const TextStyle(fontSize: 18),
                      ),
                      visualDensity: const VisualDensity(horizontal: 2, vertical: 2),
                      minLeadingWidth: 10,
                    );
                  },
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
关键修改点
  • 移除了最外层的SingleChildScrollView和嵌套的ListView,改用Column+Expanded布局,让标签页内容区域占满屏幕剩余空间,避免滚动事件冲突。
  • 去掉了标签页内列表的shrinkWrap: true,默认情况下ListView会占满父容器空间,自然支持滚动。
  • 删除了第二个标签页列表的physics: const NeverScrollableScrollPhysics(),恢复默认滚动行为。
  • 调整了顶部内容的padding,让布局更规整。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 11:24:50