flutter:使用listview之四:返回顶部(flutter 3.7.0)

一,配置用到的第三方库

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  dio: ^4.0.6
  flutter_screenutil: ^5.6.1
  fluttertoast: ^8.1.3

说明:刘宏缔的架构森林是一个专注架构的博客,

网站:https://blog.imgtouch.com
原文: https://blog.imgtouch.com/index.php/2023/06/04/flutter-shi-yong-listview-zhi-si-fan-hui-ding-bu-flutter-3-7/

         对应的源码可以访问这里获取: https://github.com/liuhongdi/
         或: https://gitee.com/liuhongdi

说明:作者:刘宏缔 邮箱: 371125307@qq.com

二,代码:

1,ListOne.dart

class ListOne {
  String title;
  String author;
  int id;

  ListOne(this.title,this.author, this.id) {}

  ListOne.fromJson(Map<String, dynamic>json)
      :title=json["title"],
        author = json["author"],
        id = json["id"];
}

2,component/backtotop.dart   封装了返回顶部的按钮

import 'package:flutter/material.dart';

class BackToTop extends StatefulWidget {
  final ScrollController controller;
  ///传入距离底部的距离
  double bottom = 0;

  BackToTop(this.controller);

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

class _BackToTopState extends State<BackToTop> {
  bool shown = false;

  double _rightPos = -40;

  @override
  void initState() {
    super.initState();
    widget.controller.addListener(isScroll);
    print("_rightPos:"+_rightPos.toString());
  }

  @override
  void dispose() {
    super.dispose();
    widget.controller.removeListener(isScroll);
  }

  void isScroll() {
    final bool toShow = (widget.controller.offset > 0);

    if (toShow == true) {
      _rightPos = 20;
    } else {
      _rightPos = -60;
    }

    setState(() {
      print("_rightPos:"+_rightPos.toString());
    });
  }

  @override
  Widget build(BuildContext context) {
    double destBottom = 0;
    if (widget.bottom == null) {
      destBottom = 0;
    } else {
      destBottom = 40;
    }

    return AnimatedPositioned(
        duration: Duration(milliseconds: 500),
        bottom: MediaQuery.of(context).padding.bottom + destBottom,
        right: _rightPos,
          child: GestureDetector(
            onTap: () {
              widget.controller.animateTo(0,
                  duration: Duration(milliseconds: 500),
                  curve: Curves.easeIn);
            },
            child: Container(
                height: 44,
                width: 44,
                alignment: Alignment(0, 0),
                decoration: new BoxDecoration(
                    color: Colors.white,
                    borderRadius: BorderRadius.all(Radius.circular(16)),
                    boxShadow: [
                      BoxShadow(
                          color: Color(0xFF000000).withOpacity(0.1),
                          blurRadius: 4,
                          spreadRadius: 0),
                    ]),
                child: Column(
                  children: <Widget>[
                    Container(
                      margin: EdgeInsets.only(top: 4),
                      child: Icon(
                        Icons.vertical_align_top,
                        size: 20,
                        color: Colors.black38,
                      ),
                    ),
                    Container(
                      margin: EdgeInsets.only(top: 0),
                      child: Text(
                        'Top',
                        style: TextStyle(fontSize: 10, color: Color(0xFFA1A6AA)),
                      ),
                    )
                  ],
                )
            ),
          ),
        );
  }
}

3,list.dart

import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
import 'package:demolistmore/model/ListOne.dart';
import 'dart:convert';
import 'package:flutter/widgets.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:demolistmore/component/backtotop.dart';
class ItemList extends StatefulWidget { @override _MyList createState()=> _MyList(); } //封装的动态页面类 class _MyList extends State<ItemList>{ ScrollController _scrollController = ScrollController(); //listview 的控制器 //存放数据 List<ListOne> _listData=[]; //当前第几页 int _currentPage = 0; int _total = 1; //上拉加载更多的提示文本 String loadMoreText = "正在加载中..."; //上拉加载更多的样式 TextStyle loadMoreTextStyle = new TextStyle(color: const Color(0xFF4483f6), fontSize: 14.0); //http方式获取数据 Future<Null> getHttp() async{ String url = 'http://api.lhdtest.com/item/list?page='+_currentPage.toString(); print(url); try { var response = await Dio().get(url); //处理json到类中 var resoMap=json.decode(response.toString()); _total = resoMap['data']['total']; for (int i = 0; i < (resoMap['data']['list'].length); i++) { var itemOne = resoMap['data']['list'][i]; ListOne goodsOne = new ListOne(itemOne['title'],itemOne['author'], itemOne['id']); _listData.add(goodsOne); } setState(() { }); } catch (e) { print(e); } }  @override void initState() { // TODO: implement initState super.initState(); getHttp(); _scrollController.addListener(() { print("_scrollController.addListener"); if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { print("开始加载数据:_currentPage:"+_currentPage.toString()); print("开始加载数据:_total:"+_total.toString()); //已经滑到底了 if (_currentPage < _total) { //还有数据,加载下一页  setState(() { loadMoreText = "正在加载中..."; loadMoreTextStyle = new TextStyle(color: const Color(0xFF4483f6), fontSize: 14.0); });  _loadMore(); } else { setState(() { loadMoreText = "没有更多数据"; loadMoreTextStyle = new TextStyle(color: const Color(0xFF999999), fontSize: 14.0); }); } } }); } //构造list的item Widget _buildRow(int index) { if (index == 5) { return _imageRow(index); } else { return _textRow(index); } } Widget _imageRow(int index) { String imageUrl = "https://images.taboola.com.cn/taboola/image/fetch/f_jpg%2Cq_auto%2Ch_350%2Cw_420%2Cc_fill%2Cg_faces:auto%2Ce_sharpen/http%3A//cdn.taboola.com/libtrc/static/thumbnails/7dc5d37f2949e30163c870e0e5585592.jpg"; return Container( margin: EdgeInsets.all(0), color: Colors.white, child: Container( width:560.w, height:360.w, margin: EdgeInsets.fromLTRB(20.w, 20.w, 20.w, 0),  decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(30.w)), color: Colors.grey.withAlpha(40), ), child:Row( children:[ Container(width:20.w), Image.network(imageUrl,height: 320.w,), ] ), alignment: Alignment.centerLeft, ), ); } Widget _textRow(int index) { //非最后一行 return Container( margin: EdgeInsets.all(0), color: Colors.white, child: Container( width:560.w, height:160.w, margin: EdgeInsets.fromLTRB(20.w, 20.w, 20.w, 0), //color: Colors.cyan,  decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(30.w)), color: Colors.grey.withAlpha(40), ), child:Row( children:[ _titleWrapper(context, _listData[index].title), _authorWrapper(context, _listData[index].author), ] ), ), ); } Widget _authorWrapper(BuildContext context, String text) { return Container( height: 160.w, width:150.w, margin: EdgeInsets.fromLTRB(0.w, 0, 0, 0), decoration: new BoxDecoration( //color: Colors.yellow,  ), alignment: Alignment.centerLeft, child: Text( text, maxLines: 2, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.headline6, ), ); } Widget _titleWrapper(BuildContext context, String text) { return Container( height: 160.w, width:500.w, margin: EdgeInsets.fromLTRB(20.w, 0, 0, 0), decoration: new BoxDecoration( //color: Colors.red,  ), alignment: Alignment.centerLeft, child: Text( text, maxLines: 2, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.headline6, ), ); } @override void dispose() { _scrollController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { var content; if (_listData.length == 0) { content = new Center( // 可选参数 child: child: new CircularProgressIndicator(), ); } else { content = _contentList(); } //设置尺寸(填写设计中设备的屏幕尺寸)如果设计基于750dp * 1334dp的屏幕  ScreenUtil.init( context, designSize: Size(750, 1334) ); return Scaffold( appBar: AppBar(title: Text('小学三年级部编版'),), body: content, ); } Widget _contentList() { return Stack( children: [ new RefreshIndicator( onRefresh: _onRefresh, child: ListView.builder( itemCount: _listData.length+1, //增加了一个上拉加载更多的指示 itemBuilder: (BuildContext context, int index) { if (index == _listData.length) { return _buildProgressMoreIndicator(); } else { return getItem(context,index); } }, controller: _scrollController, )),
//返回顶部的按钮,参数是控制器
BackToTop(_scrollController), ], ); } //显示上拉加载更多的指示 Widget _buildProgressMoreIndicator() { return new Padding( padding: const EdgeInsets.all(15.0), child: new Center( child: new Text(loadMoreText, style: loadMoreTextStyle), ), ); } //上拉加载更多的方法 Future<void> _loadMore() async { print('加载更多'); if (_currentPage >= _total) { Fluttertoast.showToast( msg: "已经到达最后一页", toastLength: Toast.LENGTH_LONG, //toastLength: 60, gravity: ToastGravity.CENTER, timeInSecForIosWeb: 1, backgroundColor: Colors.blue, textColor: Colors.white, fontSize: 20.0 ); } else { if (_currentPage < 1) { _currentPage = 1; } _currentPage += 1; getHttp(); await Future.delayed(Duration(milliseconds: 250), () { print('refresh'); }); } } //下拉刷新执行 Future<void> _onRefresh() async { print('执行刷新'); this._listData.clear(); _currentPage = 1; getHttp(); await Future.delayed(Duration(milliseconds: 500), () { print('refresh'); }); } Widget getItem(BuildContext context,int index) { return new GestureDetector( child:_buildRow(index), ); } }

4,接口返回的数据形式:

三,测试效果

四,查看flutter的版本:

liuhongdi@liuhongdideMBP ~ % flutter --version
Flutter 3.7.0 • channel stable • https://github.com/flutter/flutter.git
Framework • revision b06b8b2710 (2 weeks ago) • 2023-01-23 16:55:55 -0800
Engine • revision b24591ed32
Tools • Dart 2.19.0 • DevTools 2.20.1

 

posted @ 2023-02-10 15:56  刘宏缔的架构森林  阅读(545)  评论(0编辑  收藏  举报