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

Flutter如何实现全局横向+纵向滚动的动态CheckboxListTile?

解决Flutter全局横向+纵向同步滚动的复选框列表问题

要实现所有条目同步横向、纵向滚动,核心是调整滚动容器的嵌套结构,让横向滚动容器包裹整个纵向列表,而非让每个条目独立处理溢出。以下是修改后的完整代码及关键说明:

修改后的代码

import 'package:flutter/material.dart';
import 'package:qhse_auto/common_ui/button/OutlineButtonTextOnly.dart';
import 'package:qhse_auto/common_ui/button/PrimaryButtonTextOnly.dart';
import 'package:qhse_auto/common_ui/empty_padding.dart';
import 'package:qhse_auto/common_ui/widgets/common_search_filter.dart';
import 'package:qhse_auto/common_ui/widgets/common_textfield.dart';

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

  @override
  State<PopupSelectUserWidget> createState() => _PopupSelectUserWidgetState();
}

class _PopupSelectUserWidgetState extends State<PopupSelectUserWidget> {
  bool isCheck = false;
  final List<int> shades = [100, 200, 300, 400, 500, 600, 700, 800, 900];
  final ScrollController _horizontalScrollController = ScrollController();
  final ScrollController _verticalScrollController = ScrollController();

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.only(left: 24, top: 20, right: 24, bottom: 16),
      child: Wrap(
        children: [
          Container(
            width: double.infinity,
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(8),
              border: Border.all(color: Colors.grey),
            ),
            child: Padding(
              padding: const EdgeInsets.all(12),
              child: Column(
                children: [
                  const CommonTextField(
                    hint: 'Search NIK, Nama, Departemen, Jabatan',
                  ),
                  12.ph,
                  Container(
                    padding: const EdgeInsets.only(
                      left: 12,
                      right: 12,
                      top: 12,
                      bottom: 12,
                    ),
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(4),
                      border: Border.all(
                        color: const Color(0XFF7F8895).withOpacity(0.30),
                      ),
                    ),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        const Text(
                          'Anggota Team',
                          style: TextStyle(
                            fontSize: 16,
                            fontWeight: FontWeight.w600,
                          ),
                        ),
                        const Divider(
                          color: Color(0XFFE1E1E1),
                          thickness: 1.5,
                        ),
                        12.ph,
                        SizedBox(
                          height: MediaQuery.sizeOf(context).height / 2.5,
                          width: double.infinity,
                          child: Scrollbar(
                            controller: _verticalScrollController,
                            thumbVisibility: true,
                            trackVisibility: true,
                            child: Scrollbar(
                              controller: _horizontalScrollController,
                              thumbVisibility: true,
                              trackVisibility: true,
                              notificationPredicate: (notif) => notif.depth == 1,
                              child: SingleChildScrollView(
                                controller: _horizontalScrollController,
                                scrollDirection: Axis.horizontal,
                                child: SizedBox(
                                  width: MediaQuery.sizeOf(context).width,
                                  child: ListView.builder(
                                    controller: _verticalScrollController,
                                    shrinkWrap: true,
                                    itemCount: 40,
                                    itemBuilder: (context, index) {
                                      return CheckboxListTile(
                                        value: isCheck,
                                        onChanged: (value) {
                                          setState(() {
                                            isCheck = value!;
                                          });
                                        },
                                        title: Row(
                                          crossAxisAlignment: CrossAxisAlignment.start,
                                          children: [
                                            const CircleAvatar(
                                              backgroundImage: AssetImage('assets/images/ilham.jpeg'),
                                            ),
                                            4.pw,
                                            RichText(
                                              text: const TextSpan(
                                                text: 'Ristu Aji W\n',
                                                style: TextStyle(
                                                  color: Colors.black,
                                                  fontSize: 16,
                                                  fontWeight: FontWeight.w500,
                                                ),
                                                children: [
                                                  TextSpan(
                                                    text: '19786',
                                                    style: TextStyle(
                                                      color: Color(0XFF7F8895),
                                                      fontSize: 16,
                                                      fontWeight: FontWeight.w500,
                                                    ),
                                                  ),
                                                ],
                                              ),
                                            ),
                                            24.pw,
                                            RichText(
                                              text: const TextSpan(
                                                text: 'Operation\n',
                                                style: TextStyle(
                                                  color: Colors.black,
                                                  fontSize: 16,
                                                  fontWeight: FontWeight.w500,
                                                ),
                                                children: [
                                                  TextSpan(
                                                    text: 'Mobile Developersdsdsdsdsdsds',
                                                    style: TextStyle(
                                                      color: Color(0XFF7F8895),
                                                      fontSize: 16,
                                                      fontWeight: FontWeight.w500,
                                                    ),
                                                  ),
                                                ],
                                              ),
                                            ),
                                          ],
                                        ),
                                      );
                                    },
                                  ),
                                ),
                              ),
                            ),
                          ),
                        ),
                      ],
                    ),
                  ),
                  Padding(
                    padding: const EdgeInsets.symmetric(vertical: 16),
                    child: Row(
                      children: [
                        Expanded(
                          child: OutlineButtonText(
                            onTap: () {
                              Navigator.pop(context);
                            },
                            label: 'Cancel',
                          ),
                        ),
                        const SizedBox(width: 16),
                        Expanded(
                          child: PrimaryButtonTextOnly(
                            onTap: () {
                              // goNext(context);
                            },
                            label: 'Next',
                            isActive: true,
                          ),
                        ),
                      ],
                    ),
                  )
                ],
              ),
            ),
          )
        ],
      ),
    );
  }
}

关键修改说明

  • 滚动容器嵌套顺序调整:将横向SingleChildScrollView作为外层容器,包裹整个纵向ListView.builder,确保所有列表条目随横向滚动同步移动,而非单个条目独立处理溢出。
  • 替换Column为ListView.builder:用ListView.builder替代Column+List.generate,既高效处理大量列表项,又原生支持纵向滚动,无需额外嵌套SingleChildScrollView。
  • 移除文本溢出截断:删除RichText的overflow: TextOverflow.ellipsis,让完整文本显示,由外层横向滚动容器处理超出屏幕的内容。
  • 滚动控制器绑定优化:明确横向控制器绑定外层横向滚动容器,纵向控制器绑定ListView,确保滚动行为准确对应。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 16:34:51