Flutter跨平台移动端开发丨SingleChildScrollView、ListView......

2019-05-25 09:25:03 浏览数 (1)


SingleChildScrollView(可滑动 View)

SingleChildScrollView 类似 Android 中的 scrollview ,且同样的只可包含有一个子元素

代码语言:javascript复制
  const SingleChildScrollView({
    Key key,
    this.scrollDirection = Axis.vertical,
    this.reverse = false,
    this.padding,
    bool primary,
    this.physics,
    this.controller,
    this.child,
    this.dragStartBehavior = DragStartBehavior.down,
  }) : assert(scrollDirection != null),
       assert(dragStartBehavior != null),
       assert(!(controller != null && primary == true),
          'Primary ScrollViews obtain their ScrollController via inheritance from a PrimaryScrollController widget. '
          'You cannot both set primary to true and pass an explicit controller.'
       ),
       primary = primary ?? controller == null && identical(scrollDirection, Axis.vertical),
       super(key: key);
  • key:当前元素的唯一标识符(类似于 Android 中的 id)
  • scrollDirection:滚动方向,默认是垂直
  • reverse:是否按照阅读方向相反的方向滑动。
  • padding:填充距离
  • primary:是否使用 widget 树中默认的 PrimaryScrollController 。当滑动方向为垂直方向(scrollDirection值为Axis.vertical)并且controller没有指定时,primary默认为true
  • physics:此属性接受一个ScrollPhysics对象,它决定可滚动Widget如何响应用户操作,比如用户滑动完抬起手指后,继续执行动画;或者滑动到边界时,如何显示。默认情况下,Flutter会根据具体平台分别使用不同的ScrollPhysics对象,应用不同的显示效果,如当滑动到边界时,继续拖动的话,在iOS上会出现弹性效果,而在Android上会出现微光效果。如果你想在所有平台下使用同一种效果,可以显式指定,Flutter SDK中包含了两个ScrollPhysics的子类可以直接使用: ClampingScrollPhysics→Android下微光效果 / BouncingScrollPhysics→iOS下弹性效果
  • controller:此属性接受一个ScrollController对象。ScrollController的主要作用是控制滚动位置和监听滚动事件
  • child:子元素
代码语言:javascript复制
import 'package:flutter/material.dart';

/**
 * @des Scroll Widget
 * @author liyongli 20190506
 * */
class SingleChildScrollViewWidget extends StatefulWidget{

  @override
  State<StatefulWidget> createState() => new _StackState();

}

/**
 * @des Scroll Widget State
 * @author liyongli 20190506
 * */
class _StackState extends State<SingleChildScrollViewWidget>{

  String numberStr = "12345678909876543210123456789";

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(

        appBar: new AppBar(
          title: new Text("Scroll Widget"),
        ),

        body: Scrollbar(
          child: SingleChildScrollView(
            scrollDirection: Axis.horizontal,
            child: Center(
              child: Row(
                children: numberStr.split("").map((c) => Text(c, textScaleFactor: 2.0,)).toList(),
              ),
            ),
          ),
        )
      ),
    );
  }
}
代码语言:javascript复制
import 'package:flutter/material.dart';

/**
 * @des Scroll Widget
 * @author liyongli 20190506
 * */
class SingleChildScrollViewWidget extends StatefulWidget{

  @override
  State<StatefulWidget> createState() => new _StackState();

}

/**
 * @des Scroll Widget State
 * @author liyongli 20190506
 * */
class _StackState extends State<SingleChildScrollViewWidget>{

  String numberStr = "1234567890987654321";

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(

        appBar: new AppBar(
          title: new Text("Scroll Widget"),
        ),

        body: Scrollbar(
          child: SingleChildScrollView(
            child: Center(
              child: Column(
                children: numberStr.split("").map((c) => Text(c, textScaleFactor: 2.0,)).toList(),
              ),
            ),
          ),
        )
      ),
    );
  }
}

ListView(列表 View)

ListView 可以构建一个列表视图

代码语言:javascript复制
  ListView({
    Key key,
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
    ScrollController controller,
    bool primary,
    ScrollPhysics physics,
    bool shrinkWrap = false,
    EdgeInsetsGeometry padding,
    this.itemExtent,
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
    bool addSemanticIndexes = true,
    double cacheExtent,
    List<Widget> children = const <Widget>[],
    int semanticChildCount,
    DragStartBehavior dragStartBehavior = DragStartBehavior.down,
  }) : childrenDelegate = SliverChildListDelegate(
         children,
         addAutomaticKeepAlives: addAutomaticKeepAlives,
         addRepaintBoundaries: addRepaintBoundaries,
         addSemanticIndexes: addSemanticIndexes,
       ),
       super(
         key: key,
         scrollDirection: scrollDirection,
         reverse: reverse,
         controller: controller,
         primary: primary,
         physics: physics,
         shrinkWrap: shrinkWrap,
         padding: padding,
         cacheExtent: cacheExtent,
         semanticChildCount: semanticChildCount ?? children.length,
         dragStartBehavior: dragStartBehavior,
       );
  • key:当前元素的唯一标识符(类似于 Android 中的 id)
  • scrollDirection:滚动方向,默认是垂直
  • reverse:是否按照阅读方向相反的方向滑动。
  • controller:控制器对象,主要作用是控制滚动位置和监听滚动事件
  • primary:是否使用 widget 树中默认的 PrimaryScrollController 。当滑动方向为垂直方向(scrollDirection值为Axis.vertical)并且controller没有指定时,primary默认为true
  • physics:此属性接受一个ScrollPhysics对象,它决定可滚动Widget如何响应用户操作,比如用户滑动完抬起手指后,继续执行动画;或者滑动到边界时,如何显示。默认情况下,Flutter会根据具体平台分别使用不同的ScrollPhysics对象,应用不同的显示效果,如当滑动到边界时,继续拖动的话,在iOS上会出现弹性效果,而在Android上会出现微光效果。如果你想在所有平台下使用同一种效果,可以显式指定,Flutter SDK中包含了两个ScrollPhysics的子类可以直接使用: ClampingScrollPhysics→Android下微光效果 / BouncingScrollPhysics→iOS下弹性效果
  • shrinkWrap:表示是否根据子 widget 的总长度设置 listview 的长度,默认为 false。
  • padding:填充距离
  • itemExtent:强制 listview 的 children 的长度 为 itemExtent 的值。指定 itemExtent 的值比让子元素决定自身长度在绘制时更高效,特别是在滚动位置频繁变化的状态下,因为设置 itemExtent 可以让滚动系统提前知道列表的长度。
  • addAutomaticKeepAlives:表示是否将列表项包裹在 AutomaticKeepAlive widget 中。(在懒加载时,如果设置了包裹那么在此列表项滑出屏幕外时不会被GC。如果此列表项需要自己维护 KeepAlive 状态,那么此参数需为 false)
  • addRepaintBoundaries:表示是否将列表项包裹在 RepaintBoundary 中。(当选择将列表项包裹在 RepaintBoundary 时,在滚动过程中可以避免重绘,如果此列表项需要自己维护 KeepAlive 状态,那么此参数需为 false)
  • addSemanticIndexes:表示是否给子元素添加索引,默认为 true
  • cacheExtent:设置预加载的区域,范围在窗口可见范围之前与之后。如果设置为 0.0,表示关闭预加载
  • children:列表项集合
  • semanticChildCount:提供语义信息的孩子的数量
item 数量固定的 listview 示例

listview 构造方法中的参数 children 表示子列表集,使用这种方式构建列表需要我们提前准备好子 widget 集合。这种方式只适合实现少量且数量固定的列表展示需求

代码语言:javascript复制
import 'package:flutter/material.dart';


/**
 * @des Listview Widget
 * @author liyongli 20190506
 * */
class ListViewWidget extends StatefulWidget{

  @override
  State<StatefulWidget> createState() {
    return new _ListViewWigetState();
  }

}

/**
 * @des Listview Widget State
 * @author liyongli 20190506
 * */
class _ListViewWigetState extends State<ListViewWidget>{

  @override
  Widget build(BuildContext context) {
    return Scaffold(

        appBar: new AppBar(
          title: new Text("Scroll Widget"),
        ),

        body: ListView(
          shrinkWrap: true,
          padding: const EdgeInsets.all(20.0),
          children: <Widget>[
            const Text("1111111111111111111111111111111111", style: TextStyle(color: Colors.blue)),
            const Text("2222222222222222222222222222222222", style: TextStyle(color: Colors.blue)),
            const Text("3333333333333333333333333333333333", style: TextStyle(color: Colors.blue)),
            const Text("4444444444444444444444444444444444", style: TextStyle(color: Colors.blue)),
            const Text("5555555555555555555555555555555555", style: TextStyle(color: Colors.blue)),
            const Text("6666666666666666666666666666666666", style: TextStyle(color: Colors.blue)),
            const Text("7777777777777777777777777777777777", style: TextStyle(color: Colors.blue)),
            const Text("8888888888888888888888888888888888", style: TextStyle(color: Colors.blue)),
          ],
        ),
    );
  }

}
ListView.builder

当 listview 的列表项较多或数量未知时,就需要使用 ListView.builder 来构建列表了

代码语言:javascript复制
import 'package:flutter/material.dart';


/**
 * @des Listview.builder Widget
 * @author liyongli 20190506
 * */
class ListViewBuilderWidget extends StatefulWidget{

  @override
  State<StatefulWidget> createState() {
    return new _ListViewBuilderWidget ();
  }

}

/**
 * @des Listview.builder Widget State
 * @author liyongli 20190506
 * */
class _ListViewBuilderWidget  extends State<ListViewBuilderWidget>{

  @override
  Widget build(BuildContext context) {
    return Scaffold(

        appBar: new AppBar(
          title: new Text("ListviewBuilder Widget"),
        ),

        body: ListView.builder(
            itemCount: 100,
            itemExtent: 50.0, //强制高度为50.0
            itemBuilder: (BuildContext context, int index) {
              return ListTile(title: Text(" $index - "));
            }
        ),
    );
  }

}
ListView.separated

当 listview 的 item 间需要分割线时,我们就需要用到 ListView.separated

代码语言:javascript复制
import 'package:flutter/material.dart';


/**
 * @des Listview.builder Widget
 * @author liyongli 20190506
 * */
class ListViewBuilderWidget extends StatefulWidget{

  @override
  State<StatefulWidget> createState() {
    return new _ListViewBuilderWidget ();
  }

}

/**
 * @des Listview.builder Widget State
 * @author liyongli 20190506
 * */
class _ListViewBuilderWidget  extends State<ListViewBuilderWidget>{

  @override
  Widget build(BuildContext context) {
    return Scaffold(

        appBar: new AppBar(
          title: new Text("ListviewBuilder Widget"),
        ),

        body: ListView.separated(
            itemCount: 100,
            itemBuilder: (BuildContext context, int index) {
              return ListTile(title: Text(" $index - ", style: TextStyle(color: Colors.blue),));
            },

            separatorBuilder: (BuildContext context, int index) {
              return Divider(color: Colors.blue, height: 10,);
            }
        ),
    );
  }

}
ListView 分页加载

工程 yaml 文件中要添加 english_words 的依赖

代码语言:javascript复制
  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  english_words: ^3.1.0
代码语言:javascript复制
import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';


/**
 * @des Listview  Widget
 * @author liyongli 20190507
 * */
class InfiniteListView extends StatefulWidget {
  @override
  _InfiniteListViewState createState() => new _InfiniteListViewState();
}

/**
 * @des Listview  Widget State
 * @author liyongli 20190507
 * */
class _InfiniteListViewState extends State<InfiniteListView> {
  static const loadingTag = "##loading##"; //表尾标记
  var _words = <String>[loadingTag];

  @override
  void initState() {
    _retrieveData();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("listview 分页加载"),
        ),

        body: ListView.separated(
          itemCount: _words.length,
          itemBuilder: (context, index) {
            //如果到了表尾
            if (_words[index] == loadingTag) {
              //不足100条,继续获取数据
              if (_words.length - 1 < 100) {
                //获取数据
                _retrieveData();
                //加载时显示loading
                return Container(
                  padding: const EdgeInsets.all(16.0),
                  alignment: Alignment.center,
                  child: SizedBox(
                      width: 24.0,
                      height: 24.0,
                      child: CircularProgressIndicator(strokeWidth: 2.0)
                  ),
                );
              } else {
                //已经加载了100条数据,不再获取数据。
                return Container(
                    alignment: Alignment.center,
                    padding: EdgeInsets.all(16.0),
                    child: Text("没有更多了", style: TextStyle(color: Colors.grey),)
                );
              }
            }
            //显示单词列表项
            return ListTile(title: Text(_words[index], style: TextStyle(color: Colors.blue),));
          },
          separatorBuilder: (context, index) => Divider(height: 1, color: Colors.blue,),
        ),
      ),

    );
  }

  void _retrieveData() {
    Future.delayed(Duration(seconds: 2)).then((e) {
      _words.insertAll(_words.length - 1,
          //每次生成20个单词
          generateWordPairs().take(20).map((e) => e.asPascalCase).toList()
      );
      setState(() {
        //重新构建列表
      });
    });
  }

}

GridView(网格 View)

GridView 可以构建一个网格列表视图

代码语言:javascript复制
  GridView.builder({
    Key key,
    Axis scrollDirection = Axis.vertical,
    bool reverse = false,
    ScrollController controller,
    bool primary,
    ScrollPhysics physics,
    bool shrinkWrap = false,
    EdgeInsetsGeometry padding,
    @required this.gridDelegate,
    @required IndexedWidgetBuilder itemBuilder,
    int itemCount,
    bool addAutomaticKeepAlives = true,
    bool addRepaintBoundaries = true,
    bool addSemanticIndexes = true,
    double cacheExtent,
    int semanticChildCount,
  }) : assert(gridDelegate != null),
       childrenDelegate = SliverChildBuilderDelegate(
         itemBuilder,
         childCount: itemCount,
         addAutomaticKeepAlives: addAutomaticKeepAlives,
         addRepaintBoundaries: addRepaintBoundaries,
         addSemanticIndexes: addSemanticIndexes,
       ),
       super(
         key: key,
         scrollDirection: scrollDirection,
         reverse: reverse,
         controller: controller,
         primary: primary,
         physics: physics,
         shrinkWrap: shrinkWrap,
         padding: padding,
         cacheExtent: cacheExtent,
         semanticChildCount: semanticChildCount ?? itemCount,
       );
  • key:当前元素的唯一标识符(类似于 Android 中的 id)
  • scrollDirection:滚动方向,默认是垂直
  • reverse:是否按照阅读方向相反的方向滑动。
  • controller:控制器对象,主要作用是控制滚动位置和监听滚动事件
  • primary:是否使用 widget 树中默认的 PrimaryScrollController 。当滑动方向为垂直方向(scrollDirection值为Axis.vertical)并且controller没有指定时,primary默认为true
  • physics:此属性接受一个ScrollPhysics对象,它决定可滚动Widget如何响应用户操作,比如用户滑动完抬起手指后,继续执行动画;或者滑动到边界时,如何显示。默认情况下,Flutter会根据具体平台分别使用不同的ScrollPhysics对象,应用不同的显示效果,如当滑动到边界时,继续拖动的话,在iOS上会出现弹性效果,而在Android上会出现微光效果。如果你想在所有平台下使用同一种效果,可以显式指定,Flutter SDK中包含了两个ScrollPhysics的子类可以直接使用: ClampingScrollPhysics→Android下微光效果 / BouncingScrollPhysics→iOS下弹性效果
  • shrinkWrap:表示是否根据子 widget 的总长度设置 listview 的长度,默认为 false。
  • padding:填充距离
  • itemCount:子元素数量
  • addAutomaticKeepAlives:表示是否将列表项包裹在 AutomaticKeepAlive widget 中。(在懒加载时,如果设置了包裹那么在此列表项滑出屏幕外时不会被GC。如果此列表项需要自己维护 KeepAlive 状态,那么此参数需为 false)
  • addRepaintBoundaries:表示是否将列表项包裹在 RepaintBoundary 中。(当选择将列表项包裹在 RepaintBoundary 时,在滚动过程中可以避免重绘,如果此列表项需要自己维护 KeepAlive 状态,那么此参数需为 false)
  • addSemanticIndexes:表示是否给子元素添加索引,默认为 true
  • cacheExtent:设置预加载的区域,范围在窗口可见范围之前与之后。如果设置为 0.0,表示关闭预加载
  • semanticChildCount:提供语义信息的孩子的数量
GridView 固定列数
代码语言:javascript复制
import 'package:flutter/material.dart';

/**
 * @des GridView 固定列数
 * @author liyongli 20190508
 * */
class GridViewTest extends StatefulWidget{

  @override
  State<StatefulWidget> createState() => new _GridViewState();

}

/**
 * @des GridView Widget State
 * @author liyongli 20190508
 * */
class _GridViewState extends State<GridViewTest>{

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(

        appBar: new AppBar(
          title: new Text("GridView 固定列数"),
        ),

        body: GridView.count(
          crossAxisCount: 4,
          childAspectRatio: 0.7,
          children: <Widget>[
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
          ],
        ),
      ),
    );
  }
}
代码语言:javascript复制
import 'package:flutter/material.dart';

/**
 * @des GridView 等分宽度
 * @author liyongli 20190508
 * */
class GridViewTest extends StatefulWidget{

  @override
  State<StatefulWidget> createState() => new _GridViewState();

}

/**
 * @des GridView Widget State
 * @author liyongli 20190508
 * */
class _GridViewState extends State<GridViewTest>{

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(

        appBar: new AppBar(
          title: new Text("GridView 等分宽度"),
        ),

        body: GridView.extent(
          maxCrossAxisExtent: 200,
          childAspectRatio: 1.0,
          children: <Widget>[
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
            Icon(Icons.add_a_photo),
          ],
        ),
      ),
    );
  }
}
GridView 分页加载
代码语言:javascript复制
import 'package:flutter/material.dart';

/**
 * @des GridView 分页加载
 * @author liyongli 20190508
 * */
class GridViewTest extends StatefulWidget{

  @override
  State<StatefulWidget> createState() => new _GridViewState();

}

/**
 * @des GridView Widget State
 * @author liyongli 20190508
 * */
class _GridViewState extends State<GridViewTest>{

  List<IconData> _iconList = [];

  @override
  void initState() {
    _getData();
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new Scaffold(

        appBar: new AppBar(
          title: new Text("GridView 分页加载"),
        ),

        body: GridView.builder(
          itemCount: _iconList.length,
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 5, childAspectRatio: 1.0),
            itemBuilder: (context, index){
              if(index == _iconList.length - 1 && _iconList.length < 200){
                _getData();
              }
              return Icon(_iconList[index]);
            }
        )
      ),
    );
  }

  // 请求数据
  void _getData(){
    Future.delayed(Duration(milliseconds: 200)).then((e) {
      setState(() {
        _iconList.addAll([
          Icons.add_a_photo,
          Icons.add_a_photo,
          Icons.add_a_photo,
          Icons.add_a_photo, Icons.cake,
          Icons.add_a_photo
        ]);
      });
    });
  }
}

CustomScrollView(自定义滑动 View)

Sliver

Sliver 是分片、分区的意思。当我们需要将不同的可滑动组件组合在一起时,就需要使用此对象来完成。ListView 和 GridView 都有对应的组合对象如:SliverList 和 SliverGrid。

代码语言:javascript复制
import 'package:flutter/material.dart';

/**
 * @des CustomScrollView Widget
 * @author liyongli 20190509
 * */
class CustomScrollViewTest extends StatelessWidget{


  @override
  Widget build(BuildContext context) {
    return Material(
      child: CustomScrollView(
        slivers: <Widget>[

          // 跟随页面滑动的导航栏
          SliverAppBar(
            pinned: true, // 是否固定
            expandedHeight: 200.0, // 高度
            flexibleSpace: FlexibleSpaceBar(
              title: Text("title"),
              background: Image.asset("images/custom_scroll_title.png", fit: BoxFit.cover,)
            ),
          ),

          // Grid
          SliverPadding(
            padding: EdgeInsets.all(10.0),
            sliver: SliverGrid(
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 3, // 4行
                mainAxisSpacing: 20.0,
                crossAxisSpacing: 10.0,
                childAspectRatio: 5.0,
              ),
              delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index){
                    return Container(
                      alignment: Alignment.center,
                      color: Colors.blue,
                      child: Text("grid Item $index", style: TextStyle(color: Colors.white),),
                    );
                  },
                childCount: 12
              ),
            ),
          ),

          // List
          SliverFixedExtentList(
            itemExtent: 20,
            delegate: SliverChildBuilderDelegate((BuildContext context , int index){
              return Container(
                alignment: Alignment.center,
                color: Colors.blue,
                child: Text("list Item $index", style: TextStyle(color: Colors.white),),
              );
            },
              childCount: 30
            ),
          ),
        ],
      ),
    );
  }

}

ScrollController(控制器)

可设置滑动 View 的滚动位置,还可监听并获取滑动 View 的滚动状态及数据

代码语言:javascript复制
  ScrollController({
    double initialScrollOffset = 0.0,
    this.keepScrollOffset = true,
    this.debugLabel,
  }) : assert(initialScrollOffset != null),
       assert(keepScrollOffset != null),
       _initialScrollOffset = initialScrollOffset;
  • initialScrollOffset:初始位置
  • keepScrollOffset:是否保存滚动位置
  • ScrollController.jumpTo(0.0):直接滚动至指定位置
  • ScrollController.animateTo(0.0, duration: Duration(milliseconds: 500), curve: Curves.decelerate):带动画滚动至指定位置
代码语言:javascript复制
import 'package:flutter/material.dart';

/**
 * des ScrollController Test
 * @author liyongli 20190513
 * */
class ScrollControllerTest extends StatefulWidget{

  @override
  State<StatefulWidget> createState() {
    return new _ScrollControllerTestState();
  }

}

/**
 * des ScrollController Test State
 * @author liyongli 20190513
 * */
class _ScrollControllerTestState extends State<ScrollControllerTest>{

  ScrollController _controller = new ScrollController();
  double oldOffset = -1;

  @override
  void initState() {
    super.initState();

    _controller.addListener((){
      print("$_controller.offset"   " / "   "$oldOffset");

      if(oldOffset > _controller.offset){
        print("向下滑");
      }else{
        print("向上滑");
      }

      oldOffset = _controller.offset;
    });

  }

  @override
  void dispose() {
    super.dispose();
    _controller.dispose(); // 释放资源
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(

        appBar: AppBar(
          title: Text("ScrollController"),

        ),

        body: Scrollbar(
            child: ListView.builder(
                controller: _controller, // 控制器
                itemCount: 50, // item count
                itemExtent: 100.0, // item height
                itemBuilder: (context, index){
                  return ListTile(title:Text("$index", style: TextStyle(color: Colors.blue),));
                }
            )
        ),

        floatingActionButton: FloatingActionButton(
          onPressed: _up,
          child: Icon(Icons.arrow_upward)
        ),
      ),
    );
  }

//  滚动
  void _up(){
    // 带动画滚动
    _controller.animateTo(0.0, duration: Duration(milliseconds: 500), curve: Curves.decelerate);
    // 无动画滚动
//    _controller.jumpTo(0.0);
  }

}

0 人点赞