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

Flutter用户订阅节目列表布局问题:重复调用组件仅显示一次

Flutter 用户个人页面组件重复显示与滚动布局问题

我在开发Flutter用户个人页面时,两次调用_userprofile(context)组件来展示用户订阅的节目,但组件只显示了一次。我需要展示6个该组件,并且布局支持滚动展开。

当前效果:
当前效果截图

现有代码

import 'package:flutter/material.dart';
import 'package:flashy_tab_bar2/flashy_tab_bar2.dart';
import 'workout_post_detail.dart';
import 'editprofile_business.dart';
import 'seeall_workout.dart';

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

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

class ClientPageState extends State<ClientPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: const Text(
          'Your profile',
          style: TextStyle(
            fontWeight: FontWeight.bold,
            color: Colors.white,
          ),
        ),
        backgroundColor: Colors.black,
      ),
      body: Padding(
        padding: const EdgeInsets.all(10),
        child: Stack(
          alignment: Alignment.bottomCenter,
          children: [
            Column(
              children: [
                const SizedBox(
                  height: 10,
                ),
                Center(
                  child: CircleAvatar(
                    radius: 60,
                    backgroundColor: Colors.white,
                    child: CircleAvatar(
                      radius: 46,
                      backgroundColor: Colors.grey.shade100,
                      child: const Text('Pic'),
                    ),
                  ),
                ),
                const SizedBox(
                  height: 10,
                ),
                const Text(
                  'Usermane',
                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
                ),
                const SizedBox(
                  height: 20,
                ),
                Container(
                  height: 60,
                  width: MediaQuery.of(context).size.width * 0.90,
                  decoration: ShapeDecoration(
                      shape: const StadiumBorder(),
                      color: Colors.grey[200],
                      shadows: [
                        BoxShadow(
                          color: Colors.grey.shade400,
                          blurRadius: 10,
                          spreadRadius: 2,
                        ),
                      ]),
                  child: const Padding(
                    padding: EdgeInsets.only(left: 25.0, right: 25),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        Text(
                          'Subscribers',
                          style: TextStyle(fontWeight: FontWeight.bold),
                        ),
                        Text(
                          '8',
                          style: TextStyle(
                            fontWeight: FontWeight.bold,
                            color: Colors.grey,
                          ),
                        ),
                      ],
                    ),
                  ),
                ),
                const SizedBox(
                  height: 20,
                ),
                const Text(
                  'PROGRAMS SUBSCRIBED',
                  style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
                ),
                const SizedBox(
                  height: 20,
                ),
                Stack(
                  children: [
                    //align your container to center here with Align widget
                    Align(
                      alignment: Alignment.center,
                      child: Container(
                        height: 300,
                        width: MediaQuery.of(context).size.width * 0.90,
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(10),
                          // shape: const StadiumBorder(),
                          color: Colors.white,
                          boxShadow: [
                            BoxShadow(
                              color: Colors.grey.withOpacity(0.5),
                              spreadRadius: 5,
                              blurRadius: 7,
                              offset: const Offset(
                                  0, 3), // changes position of shadow
                            ),
                          ],
                        ),
                      ),
                    ),
                    _userprofile(context),
                    _userprofile(context)
                  ],
                ),
              ],
            )
          ],
        ),
      ),
    );
  }
}

_userprofile(context) {
  return Padding(
    padding: const EdgeInsets.fromLTRB(16, 10, 16, 10),
    //wrap the Row in a Column and take out the Divider out of Row,
    // and put it inside the Column right below the Row
    child: Column(
      children: [
        Row(
          children: [
            CircleAvatar(
                radius: 35,
                backgroundColor: Colors.white,
                child: CircleAvatar(
                    radius: 30,
                    backgroundColor: Colors.grey.shade100,
                    child: const Text('Pic'))),
            const SizedBox(
              height: 20,
            ),
            const Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Padding(
                  padding: EdgeInsets.only(left: 8.0),
                  child: Text(
                    'username',
                    style: TextStyle(fontWeight: FontWeight.bold),
                  ),
                ),
                Padding(
                  padding: EdgeInsets.only(left: 8.0),
                  child: Text('Name of the program'),
                ),
              ],
            ),
            const SizedBox(
              width: 20,
            ),
            const Padding(
              padding: EdgeInsets.all(8.0),
            ),
            ElevatedButton(
              onPressed: () {},
              style: ButtonStyle(
                backgroundColor: WidgetStateProperty.all<Color>(Colors.black),
              ),
              child: const Text(
                'ACCESS',
                style:
                    TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
              ),
            ),
          ],
        ),
        const Divider(
          thickness: 1,
          color: Color.fromARGB(255, 172, 172, 172),
        )
      ],
    ),
  );
}

问题原因

  1. Stack布局导致组件重叠:用Stack放置_userprofile组件时,所有子组件会堆叠在同一位置,所以两次调用看起来只显示了一个。
  2. 固定高度容器限制滚动:外层容器设置了固定高度300,无法容纳6个组件,也不支持滚动。

修复后的代码

import 'package:flutter/material.dart';
import 'package:flashy_tab_bar2/flashy_tab_bar2.dart';
import 'workout_post_detail.dart';
import 'editprofile_business.dart';
import 'seeall_workout.dart';

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

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

class ClientPageState extends State<ClientPage> {
  // 模拟6个订阅节目数据
  final List<Map<String, String>> subscribedPrograms = List.generate(6, (index) => {
    'username': '用户$index',
    'programName': '健身计划$index'
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: const Text(
          'Your profile',
          style: TextStyle(
            fontWeight: FontWeight.bold,
            color: Colors.white,
          ),
        ),
        backgroundColor: Colors.black,
      ),
      body: Padding(
        padding: const EdgeInsets.all(10),
        // 外层添加SingleChildScrollView,让整个页面可滚动
        child: SingleChildScrollView(
          child: Column(
            children: [
              const SizedBox(height: 10),
              Center(
                child: CircleAvatar(
                  radius: 60,
                  backgroundColor: Colors.white,
                  child: CircleAvatar(
                    radius: 46,
                    backgroundColor: Colors.grey.shade100,
                    child: const Text('Pic'),
                  ),
                ),
              ),
              const SizedBox(height: 10),
              const Text(
                'Usermane',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
              ),
              const SizedBox(height: 20),
              Container(
                height: 60,
                width: MediaQuery.of(context).size.width * 0.90,
                decoration: ShapeDecoration(
                    shape: const StadiumBorder(),
                    color: Colors.grey[200],
                    shadows: [
                      BoxShadow(
                        color: Colors.grey.shade400,
                        blurRadius: 10,
                        spreadRadius: 2,
                      ),
                    ]),
                child: const Padding(
                  padding: EdgeInsets.only(left: 25.0, right: 25),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Text(
                        'Subscribers',
                        style: TextStyle(fontWeight: FontWeight.bold),
                      ),
                      Text(
                        '8',
                        style: TextStyle(
                          fontWeight: FontWeight.bold,
                          color: Colors.grey,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 20),
              const Text(
                'PROGRAMS SUBSCRIBED',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
              ),
              const SizedBox(height: 20),
              // 替换Stack为带背景的ListView
              Container(
                width: MediaQuery.of(context).size.width * 0.90,
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(10),
                  color: Colors.white,
                  boxShadow: [
                    BoxShadow(
                      color: Colors.grey.withOpacity(0.5),
                      spreadRadius: 5,
                      blurRadius: 7,
                      offset: const Offset(0, 3),
                    ),
                  ],
                ),
                child: ListView.builder(
                  shrinkWrap: true, // 让ListView自适应内容高度
                  physics: const NeverScrollableScrollPhysics(), // 外层已有滚动,禁止内部滚动
                  itemCount: subscribedPrograms.length,
                  itemBuilder: (context, index) {
                    return _userprofile(
                      context,
                      username: subscribedPrograms[index]['username']!,
                      programName: subscribedPrograms[index]['programName']!
                    );
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// 修改组件,支持传入动态数据
Widget _userprofile(BuildContext context, {required String username, required String programName}) {
  return Padding(
    padding: const EdgeInsets.fromLTRB(16, 10, 16, 10),
    child: Column(
      children: [
        Row(
          children: [
            CircleAvatar(
                radius: 35,
                backgroundColor: Colors.white,
                child: CircleAvatar(
                    radius: 30,
                    backgroundColor: Colors.grey.shade100,
                    child: const Text('Pic'))),
            const SizedBox(width: 20), // 修复原代码中SizedBox的height错误
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Padding(
                  padding: const EdgeInsets.only(left: 8.0),
                  child: Text(
                    username,
                    style: const TextStyle(fontWeight: FontWeight.bold),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.only(left: 8.0),
                  child: Text(programName),
                ),
              ],
            ),
            const Spacer(), // 用Spacer让按钮自动靠右对齐
            ElevatedButton(
              onPressed: () {},
              style: ButtonStyle(
                backgroundColor: WidgetStateProperty.all<Color>(Colors.black),
              ),
              child: const Text(
                'ACCESS',
                style: TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
              ),
            ),
          ],
        ),
        const Divider(
          thickness: 1,
          color: Color.fromARGB(255, 172, 172, 172),
        )
      ],
    ),
  );
}

关键修改点

  1. 外层添加SingleChildScrollView,让整个页面支持滚动。
  2. 用Container包裹ListView.builder替代原Stack,保留卡片样式的同时,动态生成6个组件。
  3. 修改_userprofile组件,支持传入动态用户名和节目名称,适配真实数据场景。
  4. 修复原代码中Row里的SizedBox错误(误用height属性),并用Spacer优化按钮布局,适配不同屏幕宽度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 07:48:11