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

Flutter:SingleChildScrollView存在下方内容却无法向下滚动

问题:SingleChildScrollView无法滚动查看全部列表内容

JSON数据解析正常,所有列表项均可加载,但SingleChildScrollView组件内下方内容(如仅露出一小部分的item6及后续项)无法向下滚动查看,怀疑是SingleChildScrollView嵌套在Container与Scaffold中导致冲突。

完整代码

import 'dart:async';
import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

Future<List<Event>> fetchEvents(http.Client client) async {
    final response = await http.get(Uri.parse('https://my-json-server.typicode.com/Evgen1987RUS/test-json/events/')); // TODO: тестовый джей сон поменять на нормальный 

  return compute(parseEvents, response.body);
}

List<Event> parseEvents(String responseBody) {
  final parsed = (jsonDecode(responseBody) as List).cast<Map<String, dynamic>>();

  return parsed.map<Event>((json) => Event.fromJson(json)).toList();
}

class Event {
    final String id;
    final String name;
    final String description;
    final String date;
    final String imgPath;

    const Event({
        required this.id,
        required this.name,
        required this.description,
        required this.date,
        required this.imgPath,
    });

  factory Event.fromJson(Map<String, dynamic> json) {
      return Event (
        id: json['id'] as String,
        name: json['name'] as String,
        description: json['description'] as String,
        date: json['date'] as String,
        imgPath: json['imgPath'] as String,
      );
  }
}

void main()
    => runApp(const SPNRApp());


class SPNRApp extends StatelessWidget {
    const SPNRApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'СПНР',
      theme: ThemeData(
        primarySwatch: Colors.amber
        ),
        home: const HomePage(
          title: 'Главная страница'
          ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key, required this.title}) : super(key: key); // make it require the title as a key value
  final String title;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title)
        ),
      body: Container(
        height: 100,
        color: Colors.amber,
        child: SingleChildScrollView(
          child: FutureBuilder<List<Event>>(
          future: fetchEvents(http.Client()),
          builder: (context, snapshot) {
            if (snapshot.hasError) {
              print('line 83 snapshot failure');
              return const Center(
                child: Text('Ошибка'),
                ); 
            }
            else if (snapshot.hasData) {
              return EventsList(events: snapshot.data!);
            }
            else {
              return const Center(
                child: CircularProgressIndicator(),
              );
            }
          },
        ),
            ),
      ),
  );
  }
}

class EventsList extends StatelessWidget {
  const EventsList({super.key, required this.events});
  final List<Event> events;

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      shrinkWrap: true,
      itemCount: events.length,
      itemBuilder: (context, int index) {
        return Text(events[index].name);
      }
    );
  }
}

问题演示

应用演示效果

解决方案

问题根源有两点:

  1. 滚动组件嵌套冲突:SingleChildScrollView 和 ListView 都是滚动组件,嵌套后会导致滚动事件被拦截,无法正常触发外层滚动。
  2. 固定高度限制:外层 Container 设置了 height:100,未给滚动组件留出合理的滚动空间逻辑支持。

方案一:移除冗余的SingleChildScrollView(推荐)

直接使用 ListView 处理滚动,无需嵌套 SingleChildScrollView,同时调整外层容器的高度设置:

修改 HomePage 的 body 部分:

body: Container(
  color: Colors.amber,
  child: FutureBuilder<List<Event>>(
    future: fetchEvents(http.Client()),
    builder: (context, snapshot) {
      if (snapshot.hasError) {
        print('line 83 snapshot failure');
        return const Center(
          child: Text('Ошибка'),
        ); 
      } else if (snapshot.hasData) {
        return EventsList(events: snapshot.data!);
      } else {
        return const Center(
          child: CircularProgressIndicator(),
        );
      }
    },
  ),
),

同时修改 EventsList 中的 ListView,去掉 shrinkWrap: true(让ListView自适应父容器高度并支持滚动):

class EventsList extends StatelessWidget {
  const EventsList({super.key, required this.events});
  final List<Event> events;

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: events.length,
      itemBuilder: (context, int index) {
        return Text(events[index].name);
      }
    );
  }
}

方案二:保留SingleChildScrollView,禁用ListView的滚动

如果必须保留嵌套结构,需要禁用 ListView 的滚动能力,避免事件冲突:

修改 EventsList 中的 ListView:

class EventsList extends StatelessWidget {
  const EventsList({super.key, required this.events});
  final List<Event> events;

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(), // 禁用ListView自身滚动
      itemCount: events.length,
      itemBuilder: (context, int index) {
        return Text(events[index].name);
      }
    );
  }
}

同时确保外层 Container 的高度设置合理,让 SingleChildScrollView 能正确计算滚动范围。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 13:43:11