SingleChildScrollView使用

        Flutter 中,SingleChildScrollView(类比Android中的ScrollView) 是一个可以滚动单个子控件的小部件。当子控件的大小超过视图时,用户可以滚动以查看所有内容。SingleChildScrollView 通常用于创建可滚动的表单、列表或任何需要垂直或水平滚动的内容。类似于Android中的ScrollView,不过它只能接收一个子组件。

SingleChildScrollView构造方法

       

const SingleChildScrollView({
    super.key,
    this.scrollDirection = Axis.vertical,
    this.reverse = false,
    this.padding,
    this.primary,
    this.physics,
    this.controller,
    this.child,
    this.dragStartBehavior = DragStartBehavior.start,
    this.clipBehavior = Clip.hardEdge,
    this.restorationId,
    this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
  }) 

 注意primary属性:

  /// {@macro flutter.widgets.scroll_view.primary}
  final bool? primary;

primary属性如果为true,表示是否使用 widget 树中默认的PrimaryScrollController。

//如果primary为null。(controller为null  && )   
 final bool effectivePrimary = primary
        ?? controller == null && PrimaryScrollController.shouldInherit(context, scrollDirection);

    final ScrollController? scrollController = effectivePrimary
        ? PrimaryScrollController.maybeOf(context)
        : controller;

SingleChildScrollView中内容不应该超过屏幕太多,否则会影响性能,因为SingleChildScrollView不支持基于 Sliver 的延迟加载模型,所以内容超出屏幕尺寸太多时,使用SingleChildScrollView将会非常昂贵(性能差),应该使用一些支持Sliver延迟加载的可滚动组件,如ListView。

基础用法

通过SingleChildScrollView 包裹一个可能超出视图尺寸的子控件

import 'dart:math' as math;

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

/// 滑动到指定位置 GlobalKey 版本
/// 基于 SingleChildScrollView 和 Column
class ScrollToIndexDemoPage2 extends StatefulWidget {
  const ScrollToIndexDemoPage2({super.key});

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

class _ScrollToIndexDemoPageState2 extends State<ScrollToIndexDemoPage2> {

  //测试数据
  String realStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";


  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("ScrollToIndexDemoPage2"),
      ),
      body: SingleChildScrollView(
        padding: EdgeInsets.all(16.0),
        child: Center(
          child: Column(
            children: realStr
                .split("")
                .map((c) => Text(
                      c,
                      textScaleFactor: 2.0,
                    ))
                .toList(),
          ),
        ),
      ),
    );
  }
}


 

通过 GlobalKey实现滑动到指定位置(SingleChildScrollView + Column)

GlobalKey的使用

GlobalKey:一个特殊的键,在整个应用范围内唯一的键,与构建的Widget树中的一个特定Element关联。

GlobalKey的作用:

1.操作或获取某个状态类(如StatefulWidget)内部状态

如果需要直接操作或获取某个状态类(如StatefulWidget)内部状态时,可以为(State)创建并分配一个GlobalKey。通过调用GlobalKey.currentState来获取关联的State对象,然后执行方法或改变状态。

2.局部刷新

如果要在不重建整个树的情况下更新单个Widget,当Widget的状态变化时,使用GlobalKey获取State,然后调用State.setState()来触发其自身的重建。

3.查找Widget

通过GlobalKey可以在Widget树中找到特定的Widget或者Element。

4.维持Widget的持久性

Flutter的热重载机制,拥有GlobalKey的Widget在重新加载时不会被销毁,而是保持原有的状态。

import 'dart:math' as math;

import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';


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

  //GlobalKey<_ScrollToIndexDemoPageState2> testKey = GlobalKey<_ScrollToIndexDemoPageState2>();

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

class _ScrollToIndexDemoPageState2 extends State<ScrollToIndexDemoPage2> {
  GlobalKey scrollKey = GlobalKey();


  ScrollController controller = ScrollController();

  List<ItemModel> dataList = [];

  //测试数据
  //String realStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

  @override
  void initState() {
    dataList.clear();
    for (int i = 0; i < 100; i++) {
      dataList.add(ItemModel(i));
    }
    super.initState();
  }

  _scrollToIndex() {
    var key = dataList[12];

    ///获取 renderBox
    RenderBox renderBox =
        key.globalKey.currentContext!.findRenderObject() as RenderBox;

    ///获取位置偏移,基于 ancestor: SingleChildScrollView 的 RenderObject()
    double dy = renderBox
        .localToGlobal(Offset.zero,
            ancestor: scrollKey.currentContext!.findRenderObject())
        .dy;

    ///计算真实位移
    var offset = dy + controller.offset;

    if (kDebugMode) {
      //print("*******$offset");
    }

    controller.animateTo(offset,
        duration: const Duration(milliseconds: 500), curve: Curves.linear);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("ScrollToIndexDemoPage2"),
      ),
      // body: SingleChildScrollView(
      //   padding: EdgeInsets.all(16.0),
      //   child: Center(
      //     child: Column(
      //       children: realStr
      //           .split("")
      //           .map((c) => Text(
      //                 c,
      //                 textScaleFactor: 2.0,
      //               ))
      //           .toList(),
      //     ),
      //   ),
      // ),
      body: SingleChildScrollView(
        key: scrollKey,
        scrollDirection: Axis.vertical,
        controller: controller,
        child: Column(
          children: dataList.map<Widget>((data) {
            return CardItem(data, key: dataList[data.index].globalKey);
          }).toList(),
        ),
      ),
      persistentFooterButtons: <Widget>[
        TextButton(
          onPressed: () async {
            _scrollToIndex();
          },
          child: const Text("Scroll to 12"),
        ),
      ],
    );
  }
}

// class _CustomWidgetState extends State<> {
//
// }

class CardItem extends StatelessWidget {
  final random = math.Random();

  final ItemModel data;

  CardItem(this.data, {super.key});

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Container(
        height: (300 * random.nextDouble()),
        alignment: Alignment.centerLeft,
        child: Container(
          margin: const EdgeInsets.all(5),
          child: Text("Item ${data.index}"),
        ),
      ),
    );
  }
}

class ItemModel {
  ///这个key是关键
  GlobalKey globalKey = GlobalKey();

  ///可以添加你的代码
  final int index;

  ItemModel(this.index);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值