點燈坊

失くすものさえない今が強くなるチャンスよ

ListView 使用 ShrinkView 自行決定高度

Sam Xiao's Avatar 2024-11-30

為了避免 Pixel Overflow,實務上會在 body 使用 SingleChildScrollView,但這會導致 ListView 無法顯示。

Version

Flutter 3.24.5

Flutter

shrinkwrap01

  • Android 與 iOS 都成功使用 ShrinkView 使 ListView 自行決定高度

GridView

import 'package:flutter/material.dart';

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

  
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  final List<String> _data = List.generate(
    3,
    (index) => 'Item ${index + 1}',
  );

  
  Widget build(BuildContext context) {
    var appBar = AppBar(
      title: const Text(
        'ListView.builder with shrinkWrap',
      ),
    );

    var listView = ListView.builder(
      itemCount: _data.length,
      padding: EdgeInsets.zero,
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(),
      itemBuilder: (context, index) {
        return GestureDetector(
          onTap: () {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(
                content: Text(
                  _data[index],
                ),
              ),
            );
          },
          child: Container(
            color: Colors.transparent,
            padding: EdgeInsets.zero,
            child: Text(
              _data[index],
            ),
          ),
        );
      },
    );

    var body = SingleChildScrollView(
      child: Padding(
        padding: const EdgeInsets.all(8.0),
        child: listView,
      ),
    );

    return Scaffold(
      appBar: appBar,
      body: body,
    );
  }
}

Line 51

var body = SingleChildScrollView(
  child: Padding(
  padding: const EdgeInsets.all(8.0),
    child: listView,
  ),
);
  • SingleChildScrollView:外部 沒有 限制高度,當內部高度大於 裝置高度時,會自動產生垂直卷軸

Line 24

var listView = ListView.builder(
  itemCount: _data.length,
  padding: EdgeInsets.zero,
  shrinkWrap: true,
  physics: const NeverScrollableScrollPhysics(),
  itemBuilder: (context, index) {
    return GestureDetector(
      onTap: () {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text(
              _data[index],
            ),
          ),
        );
      },
      child: Container(
        color: Colors.transparent,
        padding: EdgeInsets.zero,
        child: Text(
          _data[index],
        ),
      ),
    );
  },
);
  • shrinkWrap: true:由 ListView 本身內容決定高度
  • physics: const NeverScrollableScrollPhysics()ListView 不顯示卷軸

Conclusion

  • ListView 預設會有卷軸,因此會搭配無限高度,但因為 body 已經使用 SingleChildScrollView 對高度不受限,所以預設 ListView 搭配 SingleChildScrollView 會無法顯示,必須搭配 shrinkWrap: trueListView 本身內容 產生高度 才能顯示
  • ListView 本身亦有卷軸,但由於在 body 已經使用 SingleChildScrollView 有卷軸,為了避免兩層卷軸,shrinkWrap: true 通常會一起搭配 physics: const NeverScrollableScrollPhysics() 使用