Flutter速来系列23-2、SingleChildScrollView 让child滚动起来

3,004 阅读4分钟

Flutter中的SingleChildScrollView

在Flutter应用开发中,我们经常会遇到需要滚动的情况,比如一个超过屏幕高度的列表或者一个长文本。Flutter提供了多种滚动组件来满足我们的需求,其中SingleChildScrollView就是一个非常实用的滚动组件。在本篇博客中,我们将深入探索SingleChildScrollView的使用和特性。

什么是SingleChildScrollView?

SingleChildScrollView是Flutter中的一个基础滚动组件,它可以让单个组件滚动。这个组件非常适合用于只有一个直接子组件的滚动场景,比如长文本、图片或者一个自定义组件。

如何使用SingleChildScrollView?

使用SingleChildScrollView非常简单,只需要将它作为你需要滚动的组件的父组件即可。下面是一个简单的例子:

没使用 SingleChildScrollView 之前遇到的问题

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Without SingleChildScrollView'),
        ),
        body: Column(
          children: List.generate(100, (index) => Text('Item $index')), // 生成一个长列表
        ),
      ),
    );
  }
}

image.png

在这个例子中,我们生成了一个长列表,列表的长度超过了屏幕的高度。但是,因为我们没有使用SingleChildScrollView,所以超出屏幕的部分无法查看,这显然不是我们想要的效果。

使用SingleChildScrollView解决问题

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('With SingleChildScrollView'),
        ),
        body: SingleChildScrollView(
          child: Column(
            children: List.generate(100, (index) => Text('Item $index')), // 生成一个长列表
          ),
        ),
      ),
    );
  }
}

image.png

SingleChildScrollView的特性

SingleChildScrollView有一些重要的特性需要我们了解:

SingleChildScrollView属性总结表格:

属性类型描述示例
childWidgetSingleChildScrollView的子组件,通常是一个需要滚动的长组件。child: Text('A really long string')
scrollDirectionAxisSingleChildScrollView的滚动方向,可以是垂直(默认)或水平。scrollDirection: Axis.horizontal
controllerScrollController控制SingleChildScrollView的滚动位置。controller: ScrollController()
physicsScrollPhysics定义滚动的物理行为,比如滚动速度、弹性效果等。physics: BouncingScrollPhysics()
reversebool是否反向滚动,默认为false。reverse: true
paddingEdgeInsetsGeometry子组件的填充。padding: EdgeInsets.all(8.0)

Axis

SingleChildScrollView的滚动方向可以是垂直的也可以是水平的,这取决于其滚动方向(scrollDirection)属性。默认情况下,滚动方向是垂直的。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('SingleChildScrollView Demo - Axis'),
        ),
        body: SingleChildScrollView(
          scrollDirection: Axis.horizontal, // 设置滚动方向为水平
          child: Row(
            children: List.generate(50, (index) => Text('Item $index ')), // 生成一个长列表
          ),
        ),
      ),
    );
  }
}

ps:可水平方向滑动

image.png

在这个例子中,我们设置了滚动方向为水平,然后生成了一个长列表,列表的长度超过了屏幕的宽度,我们可以通过水平滚动来查看列表的全部内容。


Controller

通过控制器(controller)属性,我们可以控制SingleChildScrollView的滚动位置。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final controller = ScrollController();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('SingleChildScrollView Demo - Controller'),
        ),
        body: SingleChildScrollView(
          controller: controller, // 设置控制器
          child: Column(
            children: List.generate(50, (index) => Text('Item $index')), // 生成一个长列表
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            controller.animateTo(100, duration: Duration(seconds: 1), curve: Curves.easeInOut); // 滚动到指定位置
          },
          child: Icon(Icons.arrow_upward),
        ),
      ),
    );
  }
}

在这个例子中,我们创建了一个ScrollController,并将其设置为SingleChildScrollView的控制器。

然后我们可以通过调用ScrollController的animateTo方法来滚动到指定位置。

image.png


Physics

通过物理(physics)属性,我们可以定义滚动的物理行为,比如滚动速度、弹性效果等。

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('SingleChildScrollView Demo - Physics'),
        ),
        body: SingleChildScrollView(
          physics: BouncingScrollPhysics(), // 设置物理行为为弹性滚动
          child: Column(
            children: List.generate(100, (index) => Text('Item $index')), // 生成一个长列表
          ),
        ),
      ),
    );
  }
}

iShot_2023-07-11_11.06.14.gif

在这个例子中,我们设置了物理行为为BouncingScrollPhysics,这会使得滚动有一个弹性的效果

(显然注意到这份是有弹性效果的,没有使用physics: BouncingScrollPhysics()的是没有这个效果的)

SingleChildScrollViewListView/GridView的区别

SingleChildScrollViewListView/GridView都是Flutter中的滚动组件,它们都可以让你的内容在屏幕上滚动,但是它们的使用场景和特性有所不同。

  • SingleChildScrollView:这个组件非常适合用于只有一个直接子组件的滚动场景,比如长文本、图片或者一个自定义组件。它不支持延迟构建,也就是说,它会一次性构建所有的子组件,无论这些组件是否在屏幕上可见。因此,如果你有一个非常大的子组件列表,使用SingleChildScrollView可能会导致性能问题。

  • ListView/GridView:这些组件适合用于构建大量相同类型的子组件,比如一个商品列表或者一个图片网格。它们支持延迟构建,也就是说,它们只会构建在屏幕上可见的子组件,当你滚动屏幕时,它们会自动构建新的子组件并销毁旧的子组件。这使得ListViewGridView在处理大量数据时具有很高的性能。

结论

总的来说,SingleChildScrollView是一个非常实用的滚动组件,它可以让我们轻松地实现滚动效果。无论你是在开发一个简单的应用还是一个复杂的应用,SingleChildScrollView都是你不可或缺的工具。希望本篇博客能帮助你更好地理解和使用SingleChildScrollView。