如何使 SingleChildScrollView 从底部开始?

Posted

技术标签:

【中文标题】如何使 SingleChildScrollView 从底部开始?【英文标题】:How to make SingleChildScrollView start at the bottom? 【发布时间】:2019-10-06 10:23:34 【问题描述】:

我正在开发一个应用程序,遇到了 SingleChildScrollView 从顶部开始的问题:

如何让它像这样从底部开始:

滚动视图在topContent中实现,也就是屏幕的上半部分:

    final topContent = Stack(
      children: <Widget>[
        Container(
            padding: EdgeInsets.only(left: 10.0),
            height: MediaQuery.of(context).size.height * 0.5,
            decoration: new BoxDecoration(
              image: new DecorationImage(
                image: new AssetImage("assets/images/drones1.jpg"),
                fit: BoxFit.cover,
              ),
            )),
        Container(
          height: MediaQuery.of(context).size.height * 0.5,
          padding: EdgeInsets.all(40.0),
          width: MediaQuery.of(context).size.width,
          decoration: BoxDecoration(color: Color.fromRGBO(58, 66, 86, .9)),
          child: SingleChildScrollView(      //scroll view implemented here 
            child: Center(
              child: topContentText,
            ),
          ),
        ),
        Positioned(
          left: 8.0,
          top: 60.0,
          child: InkWell(
            onTap: () 
              Navigator.pop(context);
            ,
            child: Icon(Icons.arrow_back, color: Colors.white),
          ),
        )
      ],
    );

更新:我分配了一个控制器并按照此link's 示例尝试使用 SchedulerBinding.instance.addPostFrameCallback 为滚动控制器调用 jumpTo 函数,但它给了我一个异常,结果与以前相同它没有向下滚动:

import 'package:garuda_academy_app/Constants.dart';
import 'Lesson.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';

class DetailPage extends StatefulWidget 
  final Lesson lesson;
  DetailPage(Key key, this.lesson) : super(key: key);

  @override
  _DetailPageState createState() => _DetailPageState(lesson: lesson);

class _DetailPageState extends State<DetailPage> 
  final Lesson lesson;
  ScrollController _scrollController;
  _DetailPageState(this.lesson);

  @override                             // I use the SchedularBinding here
  void initState() 
    super.initState();
    SchedulerBinding.instance.addPostFrameCallback((_) =>
      _scrollController.jumpTo(_scrollController.position.maxScrollExtent));
  

  @override
  Widget build(BuildContext context) 
    final levelIndicator = Container(
      child: Container(
        child: LinearProgressIndicator(
            backgroundColor: Color.fromRGBO(209, 224, 224, 0.2),
            value: lesson.indicatorValue,
            valueColor: AlwaysStoppedAnimation(Colors.green)),
      ),
    );

    final course = Container(
      padding: const EdgeInsets.all(7.0),
      decoration: new BoxDecoration(
          border: new Border.all(color: Colors.white),
          borderRadius: BorderRadius.circular(5.0)),
      child: new Text(
        lesson.course,
        style: TextStyle(color: Colors.white),
      ),
    );

    final topContentText = Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: <Widget>[
        SizedBox(height: 120.0),
        Icon(
          Icons.flight,
          color: Colors.white,
          size: 40.0,
        ),
        Container(
          width: 90.0,
          child: new Divider(color: Colors.green),
        ),
        SizedBox(height: 10.0),
        Text(
          lesson.title,
          style: TextStyle(color: Colors.white, fontSize: 45.0),
        ),
        SizedBox(height: 30.0),
        Row(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            Expanded(flex: 1, child: levelIndicator),
            Expanded(
                flex: 6,
                child: Padding(
                    padding: EdgeInsets.only(left: 10.0),
                    child: Text(
                      lesson.level,
                      style: TextStyle(color: Colors.white),
                    ))),
            Expanded(flex: 1, child: course)
          ],
        ),
      ],
    );

    final topContent = Stack(
      children: <Widget>[
        Container(
            padding: EdgeInsets.only(left: 10.0),
            height: MediaQuery.of(context).size.height * 0.5,
            decoration: new BoxDecoration(
              image: new DecorationImage(
                image: new AssetImage(lesson.imagePath),
                fit: BoxFit.cover,
              ),
            )),
        Container(
          height: MediaQuery.of(context).size.height * 0.5,
          padding: EdgeInsets.all(40.0),
          width: MediaQuery.of(context).size.width,
          decoration: BoxDecoration(color: Color.fromRGBO(58, 66, 86, .9)),
          child: SingleChildScrollView(
            controller: _scrollController,      // Where I pin the ScrollController
            child: Center(
              child: topContentText,
            ),
          ),
        ),
        Positioned(
          left: 8.0,
          top: 60.0,
          child: InkWell(
            onTap: () 
              Navigator.pop(context);
            ,
            child: Icon(Icons.arrow_back, color: Colors.white),
          ),
        )
      ],
    );

    final bottomContentText = Text(
      lesson.content,
      style: TextStyle(fontSize: 18.0),
    );
    final downloadButton = Container(
        padding: EdgeInsets.symmetric(vertical: 16.0),
        width: MediaQuery.of(context).size.width,
        child: RaisedButton(
          onPressed: () => ,
          color: Color.fromRGBO(58, 66, 86, 1.0),
          child:
              Text(DETAIL_PAGE_DOWNLOAD, style: TextStyle(color: Colors.white)),
        ));
    final bottomContent = Container(
      width: MediaQuery.of(context).size.width,
      padding: EdgeInsets.all(40.0),
      child: Center(
        child: Column(
          children: <Widget>[bottomContentText, downloadButton],
        ),
      ),
    );

    return Scaffold(
      body: Column(                  
        children: <Widget>[topContent, bottomContent],
      ),
    );
    

异常:I/flutter (1567):引发了另一个异常:NoSuchMethodError:getter 'position' 被调用为 null。

【问题讨论】:

【参考方案1】:

我在定义 ScrollController 时通过进一步添加(从更新中)解决了这个问题:

  ScrollController _scrollController = new ScrollController(
    initialScrollOffset: 0.0,
    keepScrollOffset: true,
  );

【讨论】:

initialScrollOffset = 0.0 默认 但是initialScrollOffsetkeepScrollOffset 都设置为它们的默认值,它应该如何改变任何东西?【参考方案2】:

只需在 SingleChildScrollView 中添加 reverse true 即可:

SingleChildScrollView( reverse: true, child: Column(), )

【讨论】:

以上是关于如何使 SingleChildScrollView 从底部开始?的主要内容,如果未能解决你的问题,请参考以下文章

如何使 SingleChildScrollView 中的 Wrap 小部件子级在 Flutter 中扩展到全高?

Flutter:如何自动滚动到 SingleChildScrollView 的末尾

TextField 获得焦点时如何滚动到 SingleChildScrollView 的底部?

如何在 Flutter 中自动滚动到 SingleChildScrollView 内的行的位置

如何使用 ListView.separated 使整个屏幕可滚动

如何在 Flutter 中使屏幕可滚动?