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;导致可视…

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;…

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;此问题的出现是由于没有配…

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

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

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…

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

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

【Nacos源码分析01-服务注册与集群间数据是同步】

文章目录 了解CAPBASE理论Nacos支持CP还是AP集群数据同步实现集群数据一致性源码 了解CAP CAP理论的核心观点是&#xff0c;一个分布式系统无法同时完全满足一致性、可用性和分区容错性这三个特性。具体而言&#xff0c;当发生网络分区时&#xff0c;系统必须在一致性和可用性之…

计算机视觉与模式识别实验1-2 图像的形态学操作

文章目录 &#x1f9e1;&#x1f9e1;实验流程&#x1f9e1;&#x1f9e1;1.图像膨胀2.图像腐蚀3.膨胀与腐蚀的综合使用4.对下面二值图像的目标提取骨架&#xff0c;并分析骨架结构。 &#x1f9e1;&#x1f9e1;全部代码&#x1f9e1;&#x1f9e1; &#x1f9e1;&#x1f9e1…

tomcat中管理环境变量之setenv.sh

tomcat配置spring profiles springboot打包war部署到外部tomcat的时候指定profile启动 windows 在%tomcat%/bin下创建setenv.bat文件 linux 在%tomcat%/bin下创建setenv.sh文件 使用JVM参数: windows set "JAVA_OPTS%JAVA_OPTS% -Dspring.profiles.activedev"in…

Python自动化办公2.0 即将发布

第一节课&#xff1a;数据整理与清洗 第二节课&#xff1a;数据筛选、过滤与排序 第三节课&#xff1a;高级数据处理技巧 第四节课&#xff1a;数据可视化与实践案例 第五节课&#xff1a;统计分析与报表 第六节&#xff1a;常见的Excel报表 与下方的课程形成知识体系&…

vue3学习(六)

前言 接上一篇学习笔记&#xff0c;今天主要是抽空学习了vue的状态管理&#xff0c;这里学习的是vuex&#xff0c;版本4.1。学习还没有学习完&#xff0c;里面有大坑&#xff0c;难怪现在官网出的状态管理用Pinia。 一、vuex状态管理知识点 上面的方式没有写全&#xff0c;还有…

如何在Windows 10上更改默认系统字体,这里有详细步骤

Windows 10的默认系统字体Segoe UI看起来相当不错。但是,如果你有更好的替代品,你可以更改Windows 10 PC上的默认系统字体。我们将向你展示如何执行此操作。 如何使用注册表编辑器更改默认系统字体 在撰写本文时,“设置”和“控制面板”都没有更改默认系统字体的选项。这意…