flutter开发实战-下拉刷新继续下拉路由进入活动页面实现

flutter开发实战-下拉刷新继续下拉路由进入活动页面实现

很多应用都有首页通过下拉刷新,继续下拉进入新的活动会场进入方式。在Flutter中,也可以通过pull_to_refresh来实现控制刷新页,继续下拉进入新的活动会场页面

一、引入pull_to_refresh

在工程的pubspec.yaml中引入插件

  pull_to_refresh: ^2.0.0

在pull_to_refresh中有enableTwoLevel、enablePullDown、enablePullUp属性。
onRefresh:当调用下拉刷新的回调onRefresh,当拉取数据完成后可以调用_refreshController1.refreshCompleted()。
onLoading:当调用上拉加载数据的时候的回调onLoading,获取数据完成后调用_refreshController1.loadComplete();
header:下拉刷新显示的headerWidget
footer:上拉加载显示的footerWidget

插件example示例如下

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'hide RefreshIndicator, RefreshIndicatorState;
import 'package:flutter/widgets.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';/*there two example implements two level,the first is common,when twoRefreshing,header will follow the list to scrollDown,when closing,still followlist move up,the second example use Navigator and keep offset when twoLevel trigger,header can use ClassicalHeader to implments twoLevel,it provide outerBuilder(1.4.7)important point:1. open enableTwiceRefresh bool ,default is false2. _refreshController.twiceRefreshComplete() can closing the two level
*/
class TwoLevelExample extends StatefulWidget {@overrideState<StatefulWidget> createState() {// TODO: implement createStatereturn _TwoLevelExampleState();}
}class _TwoLevelExampleState extends State<TwoLevelExample> {RefreshController _refreshController1 = RefreshController();RefreshController _refreshController2 = RefreshController();int _tabIndex = 0;@overridevoid initState() {// TODO: implement initState_refreshController1.headerMode?.addListener(() {setState(() {});});WidgetsBinding.instance.addPostFrameCallback((_) {_refreshController1.position?.jumpTo(0);setState(() {});});super.initState();}@overrideWidget build(BuildContext context) {// TODO: implement buildreturn RefreshConfiguration.copyAncestor(context: context,enableScrollWhenTwoLevel: true,maxOverScrollExtent: 120,child: Scaffold(bottomNavigationBar: !_refreshController1.isTwoLevel? BottomNavigationBar(currentIndex: _tabIndex,onTap: (index) {_tabIndex = index;if (mounted) setState(() {});},items: [BottomNavigationBarItem(icon: Icon(Icons.add), label: "二级刷新例子1"),BottomNavigationBarItem(icon: Icon(Icons.border_clear), label: "二级刷新例子2")],): null,body: Stack(children: <Widget>[Offstage(offstage: _tabIndex != 0,child: LayoutBuilder(builder: (_, c) {return SmartRefresher(header: TwoLevelHeader(textStyle: TextStyle(color: Colors.white),displayAlignment: TwoLevelDisplayAlignment.fromTop,decoration: BoxDecoration(image: DecorationImage(image: AssetImage("images/secondfloor.jpg"),fit: BoxFit.cover,// 很重要的属性,这会影响你打开二楼和关闭二楼的动画效果alignment: Alignment.topCenter),),twoLevelWidget: TwoLevelWidget(),),child: CustomScrollView(physics: ClampingScrollPhysics(),slivers: <Widget>[SliverToBoxAdapter(child: Container(child: Scaffold(appBar: AppBar(),body: Column(children: <Widget>[TextButton(onPressed: () {Navigator.of(context).pop();},child: Text("点击这里返回上一页!"),),TextButton(onPressed: () {_refreshController1.requestTwoLevel();},child: Text("点击这里打开二楼!"),)],),),height: 500.0,),)],),controller: _refreshController1,enableTwoLevel: true,enablePullDown: true,enablePullUp: true,onLoading: () async {await Future.delayed(Duration(milliseconds: 2000));_refreshController1.loadComplete();},onRefresh: () async {await Future.delayed(Duration(milliseconds: 2000));_refreshController1.refreshCompleted();},onTwoLevel: (bool isOpen) {print("twoLevel opening:" + isOpen.toString());},);},),),Offstage(offstage: _tabIndex != 1,child: SmartRefresher(header: ClassicHeader(),child: CustomScrollView(physics: ClampingScrollPhysics(),slivers: <Widget>[SliverToBoxAdapter(child: Container(child: TextButton(onPressed: () {Navigator.of(context).pop();},child: Text("点击这里返回上一页!"),),color: Colors.red,height: 680.0,),)],),controller: _refreshController2,enableTwoLevel: true,onRefresh: () async {await Future.delayed(Duration(milliseconds: 2000));_refreshController2.refreshCompleted();},onTwoLevel: (bool isOpen) {if (isOpen) {print("Asd");_refreshController2.position?.hold(() {});Navigator.of(context).push(MaterialPageRoute(builder: (c) => Scaffold(appBar: AppBar(),body: Text("二楼刷新"),))).whenComplete(() {_refreshController2.twoLevelComplete(duration: Duration(microseconds: 1));});}},),)],),),);}
}class TwoLevelWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {// TODO: implement buildreturn Container(decoration: BoxDecoration(image: DecorationImage(image: AssetImage("images/secondfloor.jpg"),// 很重要的属性,这会影响你打开二楼和关闭二楼的动画效果,关联到TwoLevelHeader,如果背景一致的情况,请设置相同alignment: Alignment.topCenter,fit: BoxFit.cover),),child: Stack(children: <Widget>[Center(child: Wrap(children: <Widget>[TextButton(onPressed: () {},child: Text("登陆"),),],),),Container(height: 60.0,child: GestureDetector(child: Icon(Icons.arrow_back_ios,color: Colors.white,),onTap: () {SmartRefresher.of(context)?.controller.twoLevelComplete();},),alignment: Alignment.bottomLeft,),],),);}
}

如果在下拉进入活动页面需要路由跳转,可以在onTwoLevel回调中调用路由跳转

if (isOpen) {print("Asd");_refreshController2.position?.hold(() {});Navigator.of(context).push(MaterialPageRoute(builder: (c) => Scaffold(appBar: AppBar(),body: Text("二楼刷新"),))).whenComplete(() {_refreshController2.twoLevelComplete(duration: Duration(microseconds: 1));});}

二、下拉进入活动页面实现路由跳转

下拉一定距离后,通过路由跳转进入相应的活动页面,这里是onTwoLevel回调中push到指定页面

onTwoLevel: (bool isOpen) async {print("aaa twoLevel opening:" + isOpen.toString());if (isOpen) {Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {return TopFloorPage();})).then((value){print("onTwoLevel push then");}).whenComplete(() {print("onTwoLevel push whenComplete");refreshController.twoLevelComplete(duration: Duration(microseconds: 1));});} else {refreshController.position?.jumpTo(0);}},

下拉进入活动页面实现路由跳转完整代码如下

second_floor_page.dart

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter_app_demolab/display/pull_refresh_helper.dart';
import 'package:flutter_app_demolab/display/top_floor_page.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';class SlideTopRouteBuilder extends PageRouteBuilder {final Widget page;SlideTopRouteBuilder(this.page): super(pageBuilder: (context, animation, secondaryAnimation) => page,transitionDuration: Duration(milliseconds: 800),transitionsBuilder:(context, animation, secondaryAnimation, child) =>SlideTransition(position: Tween<Offset>(begin: Offset(0.0, -1.0), end: Offset(0.0, 0.0)).animate(CurvedAnimation(parent: animation, curve: Curves.fastOutSlowIn)),child: child,));
}class SecondFloorPage extends StatefulWidget {const SecondFloorPage({super.key});@overrideState<SecondFloorPage> createState() => _SecondFloorPageState();
}class _SecondFloorPageState extends State<SecondFloorPage> {final RefreshController _refreshController =RefreshController(initialRefresh: false);RefreshController get refreshController => _refreshController;final ScrollController _scrollController = ScrollController();double _height = 200;bool _showTopBtn = false;ScrollController get scrollController => _scrollController;bool get showTopBtn => _showTopBtn;scrollToTop() {_scrollController.animateTo(0,duration: Duration(milliseconds: 300), curve: Curves.easeOutCubic);}@overridevoid initState() {// TODO: implement initStatesuper.initState();// _scrollController.addListener(() {//   if (_scrollController.offset > _height && !_showTopBtn) {//     _showTopBtn = true;//     setState(() {});//   } else if (_scrollController.offset < _height && _showTopBtn) {//     _showTopBtn = false;//     setState(() {});//   }// });}@overridevoid dispose() {// TODO: implement disposesuper.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('SecondFloor'),),body: MediaQuery.removePadding(context: context,removeTop: false,child: Builder(builder: (_) {return RefreshConfiguration.copyAncestor(context: context,twiceTriggerDistance: kHomeRefreshHeight - 15,maxOverScrollExtent: kHomeRefreshHeight,headerTriggerDistance:80 + MediaQuery.of(context).padding.top / 3,child: SmartRefresher(controller: _refreshController,header: HomeRefreshHeader(),enableTwoLevel: true,onTwoLevel: (bool isOpen) async {print("aaa twoLevel opening:" + isOpen.toString());if (isOpen) {Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {return TopFloorPage();})).then((value){print("onTwoLevel push then");}).whenComplete(() {print("onTwoLevel push whenComplete");refreshController.twoLevelComplete(duration: Duration(microseconds: 1));});} else {refreshController.position?.jumpTo(0);}},footer: RefresherFooter(),enablePullDown: true,onRefresh: () async {await Future.delayed(Duration(milliseconds: 300));refreshController.refreshCompleted();},onLoading: () async {await Future.delayed(Duration(milliseconds: 300));refreshController.loadComplete();},enablePullUp: true,child: ListView.builder(controller: _scrollController,itemBuilder: (context, index) {return Card(child: Container(alignment: Alignment.center,height: 80,child: Text('${index + 1}'),),);},itemCount: 80,),),);},),),);}
}

top_floor_page.dart

import 'package:flutter/material.dart';class TopFloorPage extends StatefulWidget {const TopFloorPage({super.key});@overrideState<TopFloorPage> createState() => _TopFloorPageState();
}class _TopFloorPageState extends State<TopFloorPage> {@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('二楼页面'),),body: Center(child: Text('二楼页面详情'),),);}
}

pull_refresh_helper.dart

import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_app_demolab/display/top_floor_page.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';const double kHomeRefreshHeight = 180.0;class HomeSecondFloorOuter extends StatelessWidget {final Widget child;HomeSecondFloorOuter(this.child, {super.key});@overrideWidget build(BuildContext context) {return Container(height: kHomeRefreshHeight + MediaQuery.of(context).padding.top + 20,width: MediaQuery.of(context).size.width,child: Stack(children: <Widget>[CachedNetworkImage(imageUrl: "https://c-ssl.dtstatic.com/uploads/blog/202404/28/LySGQjOntqjzaBW.thumb.1000_0.jpeg",fit: BoxFit.cover,height: kHomeRefreshHeight + MediaQuery.of(context).padding.top + 20,width: MediaQuery.of(context).size.width,),Align(alignment: Alignment.bottomCenter,child: Text('跌跌撞撞中,依旧热爱这个世界.',style: TextStyle(color: Colors.white,fontSize: 22,fontWeight: FontWeight.bold,)),),Align(alignment: Alignment(0, 0.85), child: child),],),alignment: Alignment.bottomCenter,);}
}/// 由于app不管明暗模式,都是有底色
/// 所以将indicator颜色为亮色
class ActivityIndicator extends StatelessWidget {final double? radius;final Brightness? brightness;ActivityIndicator({this.radius, this.brightness});@overrideWidget build(BuildContext context) {return Theme(data: ThemeData(cupertinoOverrideTheme: CupertinoThemeData(brightness: brightness),),child: CupertinoActivityIndicator(radius: radius ?? 10));}
}/// 首页列表的header
class HomeRefreshHeader extends StatelessWidget {const HomeRefreshHeader({super.key});@overrideWidget build(BuildContext context) {var strings = RefreshLocalizations.of(context)?.currentLocalization ??EnRefreshString();return ClassicHeader(canTwoLevelText: "松开进入二楼",textStyle: TextStyle(color: Colors.white),outerBuilder: (child) => HomeSecondFloorOuter(child),twoLevelView: Container(),height: 70 + MediaQuery.of(context).padding.top / 3,refreshingIcon: ActivityIndicator(brightness: Brightness.dark),releaseText: strings.canRefreshText,);}
}/// 通用的footer
///
/// 由于国际化需要context的原因,所以无法在[RefreshConfiguration]配置
class RefresherFooter extends StatelessWidget {@overrideWidget build(BuildContext context) {return ClassicFooter(
//      failedText: S.of(context).loadMoreFailed,
//      idleText: S.of(context).loadMoreIdle,
//      loadingText: S.of(context).loadMoreLoading,
//      noDataText: S.of(context).loadMoreNoData,);}
}

最后需要在MaterialApp中配置RefreshConfiguration

  class MyApp extends StatelessWidget {const MyApp({super.key});// This widget is the root of your application.@overrideWidget build(BuildContext context) {return RefreshConfiguration(hideFooterWhenNotFull: true, //列表数据不满一页,不触发加载更多child: MaterialApp(title: 'Flutter Demo',theme: ThemeData(// This is the theme of your application.//// Try running your application with "flutter run". You'll see the// application has a blue toolbar. Then, without quitting the app, try// changing the primarySwatch below to Colors.green and then invoke// "hot reload" (press "r" in the console where you ran "flutter run",// or simply save your changes to "hot reload" in a Flutter IDE).// Notice that the counter didn't reset back to zero; the application// is not restarted.primarySwatch: Colors.blue,),home: const MyHomePage(title: 'Flutter Demo Home Page'),),);}
}

效果图如下

在这里插入图片描述

三、小结

flutter开发实战-下拉刷新继续下拉路由进入活动页面实现

学习记录,每天不停进步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/20011.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入解析HTTP方法与路由响应

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、HTTP方法概述 1. GET方法 2. POST方法 二、路由与响应处理 1. 路由的默认响应 2. 处…

SpringBoot 基于jedis实现Codis高可用访问

codis与redis的关系 codis与redis之间关系就是codis是基于多个redis实例做了一层路由层来进行数据的路由&#xff0c;每个redis实例承担一定的数据分片。 codis作为开源产品&#xff0c;可以很直观的展示出codis运维成本低&#xff0c;扩容平滑最核心的优势. 其中&#xff0…

JavaWeb笔记整理+图解——Listener监听器

欢迎大家来到这一篇章——Listener监听器 监听器和过滤器都是JavaWeb服务器三大组件&#xff08;Servlet、监听器、过滤器&#xff09;之一&#xff0c;他们对于Web开发起到了不可缺少的作用。 ps&#xff1a;想要补充Java知识的同学们可以移步我已经完结的JavaSE笔记&#x…

宏集JMobile Studio—实现HMI界面高自由度设计

一、简介 物联网HMI的组态软件是数据可视化的重要工具&#xff0c;工程师可以通过图形化界面来配置、监控和管理现场采集的数据。目前&#xff0c;市面上大多数的组态软件里的可视化控件库都由设计师预先部署&#xff0c;用户只能调用而不能完全自定义控件&#xff0c;导致可视…

点餐小程序服务器怎么选

点餐小程序服务器怎么选&#xff1f;随着科技的不断发展&#xff0c;点餐小程序已成为餐饮行业的新宠。它不仅提升了顾客的点餐体验&#xff0c;也为商家带来了更多的便利。然而&#xff0c;面对市场上琳琅满目的服务器选择&#xff0c;如何为您的点餐小程序挑选一台合适的服务…

20240531在飞凌的OK3588-C开发板上跑原厂的Buildroot测试ETH0接口【仅供参考】

20240531在飞凌的OK3588-C开发板上跑原厂的Buildroot测试ETH0接口 2024/5/31 20:28 rootrk3588-buildroot:/# ifconfig eth0 up rootrk3588-buildroot:/# ifconfig eth1 up rootrk3588-buildroot:/# ifconfig rootrk3588-buildroot:/# rootrk3588-buildroot:/# ifconfig eth1…

化妆品企业如何借助CRM客户管理应用,实现业绩飞跃?

在当今数字化时代&#xff0c;化妆品企业面临着前所未有的挑战与机遇。随着消费者需求的日益多样化和市场竞争的加剧&#xff0c;如何高效地管理客户信息、优化客户服务流程、提升客户满意度&#xff0c;成为了化妆品企业亟需解决的问题。而在这个背景下&#xff0c;低代码平台…

C#中的实体属性详解与示例

文章目录 实体属性的定义实体属性的访问实体属性的示例总结 在C#中&#xff0c;实体属性是面向对象编程的重要组成部分。实体属性允许我们定义对象的特征和行为&#xff0c;并提供了一种方式来访问和管理这些特征。通过实体属性&#xff0c;我们可以封装对象的状态&#xff0c;…

Django信号详解

Django信号&#xff08;Signals&#xff09;是一种允许应用组件在特定事件发生时进行通信的机制&#xff0c;而不必直接引用彼此。它遵循观察者模式&#xff0c;允许发送者&#xff08;如模型实例的保存或删除&#xff09;发送信号&#xff0c;而接收者&#xff08;信号处理器&…

【三维模型采集设备】轮廓扫描仪介绍

文章目录 一、声明二、轮廓扫描仪的分类1.1 按工作原理分类1.2 按应用场景分类1.3 按扫描精度分类1.4 按扫描方式分类1.5 按测量方式来分类 三、问答2.1 激光轮廓扫描仪采用红光、绿光和蓝光各有什么优缺点&#xff1f; 一、声明 本帖信息来自对网上信息的汇总&#xff0c;如有…

WiFi串口服务器与工业路由器:局域网应用的协同之力

在工业物联网&#xff08;IIoT&#xff09;迅猛发展的当下&#xff0c;局域网&#xff08;LAN&#xff09;作为连接工业设备与数据中心的桥梁&#xff0c;其重要性日益凸显。WiFi串口服务器与工业路由器作为局域网中的关键组件&#xff0c;以其独特的性能和功能&#xff0c;为传…

64位和32位对C++ 对long类型的使用造成程序崩溃、内存泄漏问题。

系列文章目录 1、理解32位和64位下long类型和int类型不同 2、理解release和debug版本编译的可执行程序的区别 3、谨慎在64位下对long类型与int类型去赋值和相互转换 文章目录 系列文章目录前言一、int、long类型二、使用步骤1.示例代码 前言 编译环境&#xff1a;qt -c、linu…

cURL error 60: SSL certificate problem: unable to get local issuer certifica

本地小程序把接口换到本地的服务器接口&#xff0c;然后就报错了&#xff1a; cURL error 60: SSL certificate problem: unable to get local issuer certificate (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) 经查询查到&#xff1a;此问题的出现是由于没有配…

OSPF重发布

OSPF A-->B [r2]ospf 1 [r2-ospf-1]import-route rip 默认进入路由为类型 2&#xff0c;种子度量为1; 重发布进入的LSA为5类/7类&#xff0c;优先级为150&#xff1b; 类型1:显示总度量种子度量沿途累加 类型2:仅显示种子度量&#xff0c;但选路时比较的是总度量种子度量沿…

Nginx企业级负载均衡:技术详解系列(14)—— 账户认证功能

你好&#xff0c;我是赵兴晨&#xff0c;97年文科程序员。 你有没有听说过Nginx的账户认证功能&#xff1f;这可不只是一个技术问题&#xff0c;它关系到我们上网时的安全和便利。就像家里需要一把钥匙才能进们一样&#xff0c;Nginx的账户认证功能就是确保有只有授权的人才能…

Go基础编程 - 03 - init函数、main函数、_(下划线)

目录 1. init 函数2. main 函数3. init 函数与 main 函数异同4. _ (下划线)示例 1. init 函数 Go语言中&#xff0c;init 函数用于包&#xff08;package&#xff09;的初始化。具有以下特征&#xff1a; 1. init 函数用于程序执行前包的初始化&#xff0c;如初始化变量等。2…

Android Studio启动第一个项目的报错——笔记

零基础第一次使用Android Studio开发遇到的问题 1 新建项目下载gradle慢的问题 直接叉掉打开gradle官网手动下载自己想要的版本 gradle官网 然后在刚刚建好的项目里找到gradle-wrapper.properties&#xff0c;修改distributionUrl&#xff0c;并把自己从官网下载的包放到和gr…

Scroll 上的明星项目Pencils Protocol ,缘何被严重低估?

近日&#xff0c;完成品牌升级的 Pencils Prtocol 结束了 Season 2 并无缝开启了 Season 3&#xff0c;在 Season 3 中&#xff0c;用户可以通过质押系列资产包括 $ETH、$USDT、$USDC、$STONE 、$wrsETH、$pufETH 等来获得可观收益&#xff0c;并获得包括 Scroll Marks、 Penci…

IO进程线程(二)文件IO(系统调用)

一、文件IO &#xff08;一&#xff09;概念 文件IO就是系统调用&#xff0c;用户空间进入内核空间的过程就是系统调用。 系统调用没有缓冲机制&#xff0c;效率较低&#xff0c;可移植性也相对较差&#xff0c;实时性高。 文件描述符是使用open函数打开文件时的返回值&…

【运维项目经历|023】Docker自动化部署与监控项目

目录 项目名称 项目背景 项目目标 项目成果 我的角色与职责 我主要完成的工作内容 本次项目涉及的技术 本次项目遇到的问题与解决方法 本次项目中可能被面试官问到的问题 问题1&#xff1a;项目周期是多久&#xff1f; 问题2&#xff1a;服务器部署架构方式及数量配置…