flutter之上拉刷新和下拉加载更多

我爱海鲸 2024-07-31 15:06:56 flutter学习

简介Dart、跨平台、dio、http

1、搭建flutter的开发环境:http://www.haijin.xyz/article/730

2、创建一个项目:

flutter create myapp

项目结构:

4、相关代码:

wallpaper_detail_api.dart:

import '../network/api_service.dart';

class WallpaperDetailApi {
  final ApiService _apiService = ApiService();
  void fetchTodoData({
    int? currentPage,
    int? pageSize,
    int? typeId,
    String? name,
    required Function(dynamic data) callBack,
})  {
    var body = {
      "currentPage":currentPage,
      "pageSize":pageSize,
      "typeId":typeId,
      "name":name,
    };
    _apiService.get(
      url: 'http://192.168.97.13:5800/api',
      body: body,
      callbackFunc:callBack,
    );
  }
}

wallpaper_detail.dart:

/// total : 55052
/// pageNo : 1
/// pageSize : 5506

class WallpaperDetail {
  WallpaperDetail({
    num? total,
    num? pageNo,
    num? pageSize,
    List<Records>? records,
  }) {
    _total = total;
    _pageNo = pageNo;
    _pageSize = pageSize;
    _records = records;
  }

  WallpaperDetail.fromJson(dynamic json) {
    _total = json['total'];
    _pageNo = json['pageNo'];
    _pageSize = json['pageSize'];
    if (json['records'] != null) {
      _records = [];
      json['records'].forEach((v) {
        _records?.add(Records.fromJson(v));
      });
    }
  }
  num? _total;
  num? _pageNo;
  num? _pageSize;
  List<Records>? _records;
  WallpaperDetail copyWith({
    num? total,
    num? pageNo,
    num? pageSize,
    List<Records>? records,
  }) =>
      WallpaperDetail(
        total: total ?? _total,
        pageNo: pageNo ?? _pageNo,
        pageSize: pageSize ?? _pageSize,
        records: records ?? _records,
      );
  num? get total => _total;
  num? get pageNo => _pageNo;
  num? get pageSize => _pageSize;
  List<Records>? get records => _records;

  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    map['total'] = _total;
    map['pageNo'] = _pageNo;
    map['pageSize'] = _pageSize;
    final records = _records;
    if (records != null) {
      map['records'] = records.map((v) => v.toJson()).toList();
    }
    return map;
  }
}

/// id : 1933
/// wallpaperName : "甜美Lolita少女壁纸"
/// wallpaperListUrl : "https://up.enterdesk.com/edpic_360_360/cb/57/b9/cb57b98151247cd5a6e5211f05e460e3.jpg"
/// wallpaperType : 1
/// remark : ""
/// wallpaperDetailUrl : "https://up.enterdesk.com/edpic_source/cb/57/b9/cb57b98151247cd5a6e5211f05e460e3.jpg"

class Records {
  Records({
    num? id,
    String? wallpaperName,
    String? wallpaperListUrl,
    num? wallpaperType,
    String? remark,
    String? wallpaperDetailUrl,
  }) {
    _id = id;
    _wallpaperName = wallpaperName;
    _wallpaperListUrl = wallpaperListUrl;
    _wallpaperType = wallpaperType;
    _remark = remark;
    _wallpaperDetailUrl = wallpaperDetailUrl;
  }

  Records.fromJson(dynamic json) {
    _id = json['id'];
    _wallpaperName = json['wallpaperName'];
    _wallpaperListUrl = json['wallpaperListUrl'];
    _wallpaperType = json['wallpaperType'];
    _remark = json['remark'];
    _wallpaperDetailUrl = json['wallpaperDetailUrl'];
  }
  num? _id;
  String? _wallpaperName;
  String? _wallpaperListUrl;
  num? _wallpaperType;
  String? _remark;
  String? _wallpaperDetailUrl;
  Records copyWith({
    num? id,
    String? wallpaperName,
    String? wallpaperListUrl,
    num? wallpaperType,
    String? remark,
    String? wallpaperDetailUrl,
  }) =>
      Records(
        id: id ?? _id,
        wallpaperName: wallpaperName ?? _wallpaperName,
        wallpaperListUrl: wallpaperListUrl ?? _wallpaperListUrl,
        wallpaperType: wallpaperType ?? _wallpaperType,
        remark: remark ?? _remark,
        wallpaperDetailUrl: wallpaperDetailUrl ?? _wallpaperDetailUrl,
      );
  num? get id => _id;
  String? get wallpaperName => _wallpaperName;
  String? get wallpaperListUrl => _wallpaperListUrl;
  num? get wallpaperType => _wallpaperType;
  String? get remark => _remark;
  String? get wallpaperDetailUrl => _wallpaperDetailUrl;

  Map<String, dynamic> toJson() {
    final map = <String, dynamic>{};
    map['id'] = _id;
    map['wallpaperName'] = _wallpaperName;
    map['wallpaperListUrl'] = _wallpaperListUrl;
    map['wallpaperType'] = _wallpaperType;
    map['remark'] = _remark;
    map['wallpaperDetailUrl'] = _wallpaperDetailUrl;
    return map;
  }
}

home_page.dart:(这部分是重点)

import 'package:flutter/material.dart';
import 'package:myapp/api/wallpaper_detail_api.dart';
import 'package:myapp/model/wallpaper_detail.dart';


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

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

class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin {
  final WallpaperDetailApi _wallpaperDetailApi = WallpaperDetailApi();

  WallpaperDetail? _wallpaper;

  List<Records> _records = [];

  // 页码
  int _currentPage = 1;
  // 是否正在刷新
  bool _isRefreshing = false;
  // 是否正在加载更多
  bool _isLoadingMore = false;

  double _listHeight = 0.0; // 列表的高度

  final ScrollController _scrollController = ScrollController();

  // 索引
  int _itemIndex = 0;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _scrollController.addListener((){
        // 滑动到底部,去做加载更多的请求
      print("_itemIndex:$_itemIndex");
      print(_records.length);
      if(_itemIndex + 5 > _records.length){
          if (!_isLoadingMore) {
            print("加载更多");
            _currentPage++;
            _fetchData();
          }
        }
    });
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  Future<void> _fetchData() async {
    try{
      print("页码数:");
      print(_currentPage);
      _wallpaperDetailApi.fetchTodoData(currentPage: _currentPage,callBack: _analyzeWallpaperData);
    } catch(e) {
      print(e);
    }

  }

  // 拉下刷新函数
  Future<void> _refreshFunc() async {
    print("刷新");
    _currentPage = 1;
    _fetchData();
  }


  // 解析获取的数据
  void _analyzeWallpaperData(dynamic data) {
      _wallpaper = WallpaperDetail.fromJson(data);
      print("获取的数据");

      if (_currentPage == 1) {
        // 如果是第一页,则替换列表
        setState(() {
          _records = _wallpaper!.records!;
        });
      } else {
        // 如果不是第一页,则追加到列表末尾
        setState(() {
          _wallpaper!.records?.forEach((item){
            _records.add(item);
          });
        });
      }
      setState(() {
        _isRefreshing = false;
        _isLoadingMore = false;
      });
  }


  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text('壁纸列表'),
      ),
      body: RefreshIndicator(
        onRefresh: _refreshFunc, // 下拉刷新
        child: _wallpaper == null
            ? Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () {
                  setState(() {
                    _currentPage = 1; // 重置页码
                    // _records.clear(); // 清空列表
                  });
                  _fetchData(); // 获取第一页数据
                },
                child: Text('获取壁纸'),
              ),
            ],
          ),
        ) : ListView.builder(
          itemCount: (_wallpaper?.pageSize??0) as int,
          itemBuilder: (context, index) {
            _itemIndex = index;
            if(index >  _currentPage * 10 - 5){
              if (!_isLoadingMore) {
                print("加载更多");
                _currentPage++;
                _fetchData();
              }
            }
            if (index < _records!.length) {
              final item = _records?[index];
              return ListTile(
                leading: Image.network(item!.wallpaperListUrl??""),
                title: Text(item!.wallpaperName??""),
                subtitle: Text('ID: ${item?.id}'),
                onTap: () {
                 print("跳转到壁纸的详情页面");
                },
              );
            } else {
              return Container(
                height: 50,
                alignment: Alignment.center,
                child: CircularProgressIndicator(),
              );
            }

          },
          // controller: _scrollController,
        ),
      ),
    );
  }

  @override
  // TODO: implement wantKeepAlive
  bool get wantKeepAlive => true;
}

滑动的核心在: itemBuilder: (context, index) 这段代码里

    if(index >  _currentPage * 10 - 5){
              if (!_isLoadingMore) {
                print("加载更多");
                _currentPage++;
                _fetchData();
              }
            }

上面的那个_scrollController也可以使用,但没必要了。

运行截图:

你好:我的2025