flutter-下拉刷新上拉加载更多以及国际化配置(pull_to_refresh)

3,336 阅读8分钟

前言

前面讲了不少滚动视图的案例,然而使用的时候肯定是避免不了下拉刷新、上拉加载更多,为了方便使用,本篇文章也是打着实际应用,而非探索原理的方式介绍

pull_to_refresh 就是一个使用率非常高的一个下拉刷新控件,设置好国际化后,可以根据地区语言使用默认的文字,下面内容缺少的可以到这里查看

测试demo地址

先看一下常见的下拉刷新效果(这里默认是英文的,后面介绍怎么更改)

image.png

再看一下上拉加载更多效果

image.png

pull_to_refresh的使用

话不多说,线上一个单个页面的刷新案例图

image.png

单个页面的应用

//使用局部刷新效果
class _DetailPageState extends State<DetailPage> {
  final RefreshController _refreshController = RefreshController(initialRefresh: false); //为 false 默认不触发刷新

  int count = 0;

  //刷新的 Future 方法
  Future onRefresh() async {
    await Future.delayed(const Duration(seconds: 1));
    setState(() {
      count = 10;
    });
    //需要使用 refreshCompleted 标记成功
    _refreshController.refreshCompleted();
    //如果要标记没有数据,可以调用 _refreshController.loadNoData(),可以点进去看其他的
  }

  //加载更多的 Future方法
  Future loadMore() async {
    await Future.delayed(const Duration(seconds: 1));
    setState(() {
      count += 10;
    });
    _refreshController.loadComplete();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("DetailPage"),
      ),
      //在 body 中添加 SmartRefresher 刷新组件
      body: SmartRefresher(
        //设置支持下拉和上拉,否则默认不支持上拉
        enablePullDown: true, //默认为true
        enablePullUp: true, //默认为false,不设置不支持上拉加载
        controller: _refreshController,
        header: const WaterDropHeader(), //这个页面是瀑布流效果,可以更换
        onRefresh: onRefresh,
        onLoading: loadMore,
        child: ListView.builder(
          itemBuilder: (context, index) {
            return Container(
              margin: const EdgeInsets.all(10),
              color: Colors.blue,
            );
          },
          itemCount: count,
          itemExtent: 80,
        ),
      )
    );
  }
}

下拉刷新全局效果设置

正常开发中,一般一个应用多个页面都是使用同一个效果,因此我们可以全局设置成一个下拉刷新效果,开始放的就是全局设置的效果(只针对 SmartRefresher组件有效果)

需要用到 RefreshConfiguration,其需要嵌套到 MaterialApp外层才可以

如下所示

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  Widget refreshConfig({ required Widget child }) {
    return RefreshConfiguration(
      headerBuilder: () => const ClassicHeader(),// 配置默认头部指示器,假如你每个页面的头部指示器都一样的话,你需要设置这个
      footerBuilder:  () => const ClassicFooter(),// 配置默认底部指示器
      headerTriggerDistance: 60.0,  // 头部触发刷新的越界距离
      footerTriggerDistance: 100, //底部触发刷新的越界距离,距离底部多少开始刷新
      springDescription: const SpringDescription(stiffness: 170, damping: 16, mass: 1.9), //弹性参数,劲度系数、阻尼、质量
      maxOverScrollExtent :100, //头部最大可以拖动的范围,如果发生冲出视图范围区域,请设置这个属性
      maxUnderScrollExtent: 0, // 底部最大可以拖动的范围,0默认底部不能往上拖拽
      enableScrollWhenRefreshCompleted: true, //这个属性不兼容PageView和TabBarView,如果你特别需要TabBarView左右滑动,你需要把它设置为true
      enableLoadingWhenFailed : true, //在加载失败的状态下,用户仍然可以通过手势上拉来触发加载更多
      hideFooterWhenNotFull: false, // Viewport不满一屏时,禁用上拉加载更多功能
      enableBallisticLoad: true, // 可以通过惯性滑动触发加载更多
      child: child
    );
  }

  @override
  Widget build(BuildContext context) {
    return refreshConfig(
      child: MaterialApp(
        title: 'Flutter Refresh Demo',
        //返回支持的语言
        theme: ThemeData(
            primarySwatch: Colors.blue,
            appBarTheme: const AppBarTheme(
              toolbarHeight: 44,
            )
        ),
        home: const HomePage(),
      ),
    );
  }
}

随便搞一个案例试试,由于逻辑都一样,我们看一下 SmartRefresher代码

Scaffold(
  appBar: AppBar(
    title: const Text("HomePage"),
  ),
  //我们不需要设置头,高度之类的,使用的默认就是全局设置
  //如果局部需要定制,那么在重新赋值新效果即可,局部优先于全局
  body: SmartRefresher(
    enablePullDown: true,
    enablePullUp: true,
    controller: _refreshController,
    onRefresh: onRefresh,
    onLoading: loadMore,
    child: ListView.builder(
      itemBuilder: (context, index) {
        return Container(
          margin: const EdgeInsets.all(10),
          color: Colors.blue,
        );
      },
      itemCount: count,
      itemExtent: 80,
    ),
  )
);

到了这里,你发现,这么简单就结束了,但怎么都是英文,并且切换了中文环境也不对,觉得我还留了一手,其实是国际化的问题

如果我们只用一个语言,那么不需要国家化,那么只需要像下面一样,给我们的头部、尾部组件的对应属性赋值,如下所示,这样就可以了

Widget refreshConfig({ required Widget child }) {
    return RefreshConfiguration(
      headerBuilder: () => const ClassicHeader(
        // idleText: "下拉刷新",
        // refreshingText: "刷新中...",
        // completeText: "加载成功",
        // releaseText: "松开立即刷新",
        // failedText: '刷新失败',
      ),// 配置默认头部指示器,假如你每个页面的头部指示器都一样的话,你需要设置这个
      footerBuilder:  () => const ClassicFooter(
        // idleText: "上拉加载",
        // loadingText: "加载中…",
        // canLoadingText: "松手开始加载数据",
        // failedText: "加载失败",
        // noDataText: "没有更多数据了", //没有内容的文字
        // noMoreIcon: ,没有内容的图标
      ),// 配置默认底部指示器
      headerTriggerDistance: 60.0,  // 头部触发刷新的越界距离
      footerTriggerDistance: 100, //底部触发刷新的越界距离,距离底部多少开始刷新
      springDescription: const SpringDescription(stiffness: 170, damping: 16, mass: 1.9), //弹性参数,劲度系数、阻尼、质量
      maxOverScrollExtent :100, //头部最大可以拖动的范围,如果发生冲出视图范围区域,请设置这个属性
      maxUnderScrollExtent: 0, // 底部最大可以拖动的范围,0默认底部不能往上拖拽
      enableScrollWhenRefreshCompleted: true, //这个属性不兼容PageView和TabBarView,如果你特别需要TabBarView左右滑动,你需要把它设置为true
      enableLoadingWhenFailed : true, //在加载失败的状态下,用户仍然可以通过手势上拉来触发加载更多
      hideFooterWhenNotFull: false, // Viewport不满一屏时,禁用上拉加载更多功能
      enableBallisticLoad: true, // 可以通过惯性滑动触发加载更多
      child: child
    );
}

到这里,还是不满意,需要国际化,下面就介绍国际化的配置操作了

国际化配置

三方文档也给出了我们一些国际化的配置操作,配置了发现会报错,且下面几个属性还有些不理解,这里就稍微详细点介绍

第一步

加入依赖,在 pubspec.yaml 文件中加入下面 dependencies 依赖,这样使用 GlobalMaterialLocalizations 就不会报错了

flutter_localizations:
    sdk: flutter

image.png

第二步

加入国际化配置参数,这个需要在我们的 MetrialApp 中设置,系统一共给我们了三个参数

localizationsDelegates、supportedLocales、localeResolutionCallback

localizationsDelegates: 国际化代理集合

supportedLocales:支持的国际化地区,可以进行限制

localeResolutionCallback:国家化地区的回调,可以根据支持的地区和当前地区进行判断使用那个地区的国际化文字

直接上代码介绍

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  //基础属性的设置,不多介绍
  Widget refreshConfig({ required Widget child }) {
    return RefreshConfiguration(
      headerBuilder: () => const ClassicHeader(),// 配置默认头部指示器,假如你每个页面的头部指示器都一样的话,你需要设置这个,这里设置成比较常见的效果
      footerBuilder:  () => const ClassicFooter(),// 配置默认底部指示器
      headerTriggerDistance: 60.0,  // 头部触发刷新的越界距离
      footerTriggerDistance: 100, //底部触发刷新的越界距离,距离底部多少开始刷新
      springDescription: const SpringDescription(stiffness: 170, damping: 16, mass: 1.9), //弹性参数,劲度系数、阻尼、质量
      maxOverScrollExtent :100, //头部最大可以拖动的范围,如果发生冲出视图范围区域,请设置这个属性
      maxUnderScrollExtent: 0, // 底部最大可以拖动的范围,0默认底部不能往上拖拽
      enableScrollWhenRefreshCompleted: true, //这个属性不兼容PageView和TabBarView,如果你特别需要TabBarView左右滑动,你需要把它设置为true
      enableLoadingWhenFailed : true, //在加载失败的状态下,用户仍然可以通过手势上拉来触发加载更多
      hideFooterWhenNotFull: false, // Viewport不满一屏时,禁用上拉加载更多功能
      enableBallisticLoad: true, // 可以通过惯性滑动触发加载更多
      child: child
    );
  }

  @override
  Widget build(BuildContext context) {
    return refreshConfig(
      child: MaterialApp(
        title: 'Flutter Refresh Demo',
        //国际化代理集合,使用了三方文档后,发现报错,前面依赖加入就没事了
        //这个国际化代理集合就是,三方的 + 系统的
        //然后系统会给出警告,有更新的要使用最新的,给了我们一个集合,我们拼接一下即可
        // getLocalizationsDelegates就是拼接了系统的国际化代理
        localizationsDelegates: getLocalizationsDelegates([
          //这行刷新组件的国际化代理
          RefreshLocalizations.delegate,
        ]),
        
        //也可以设定只支持哪些,不支持的就显示默认的,不填系统就给了一个英文
        //这个结果会返回到 localeResolutionCallback 中,便于比较判断使用哪些
        // supportedLocales: const [
        //   Locale('zh'),
        //   Locale('en'),
        // ],
        
        //如果不想支持所有的,那么可以根据当前 locale,与 supportedLocales 进行动态返回对应的locale即可
        //下面直接支持所有的即可
        localeResolutionCallback: (locale, supportedLocales) => locale,
        

        //返回支持的语言
        theme: ThemeData(
            primarySwatch: Colors.blue,
            appBarTheme: const AppBarTheme(
              toolbarHeight: 44,
            )
        ),
        home: const HomePage(),
      ),
    );
  }
  
  //系统的国际化处理 + 我们的三方的国际化代理
  Iterable<LocalizationsDelegate> 
      getLocalizationsDelegates(List<LocalizationsDelegate> newDelegates) {
    //系统的不能丢弃,将系统的几种都添加进去金科
    newDelegates.addAll(GlobalMaterialLocalizations.delegates);
    return newDelegates;
  }
}

再看我们国际化后的下拉刷新

image.png

再看一下上拉加载更多

image.png

最后

有缺少的功能或者属性,可以进到三方看具体的文档哈,或者直接点进去源码查看,相信很轻松能够解决