Flutter分页插件pag的使用

发布于 1周前 作者 vueper 来自 Flutter

Flutter分页插件PAG的使用

项目介绍

为Flutter打造的PAG动画组件,以外接纹理的方式实现。

注:如果遇到使用问题请在本仓库提issue与作者讨论,或直接提交pr参与共建。

快速上手

Flutter侧通过PAGView来使用动画。

引用

pubspec.yaml文件中添加依赖:

dependencies:
  pag: 1.0.0

Android端混淆文件中配置,避免影响:

-keep class org.libpag.**{*;}

使用本地资源

PAGView.asset(
    "assets/xxx.pag", // flutter侧资源路径
    repeatCount: PagView.REPEAT_COUNT_LOOP, // 循环次数
    initProgress: 0.25, // 初始进度
    key: pagKey,  // 利用key进行主动调用
    autoPlay: true, // 是否自动播放
)

使用网络资源

PAGView.url(
    "xxxx", // 网络链接
    repeatCount: PagView.REPEAT_COUNT_LOOP, // 循环次数
    initProgress: 0.25, // 初始进度
    key: pagKey,  // 利用key进行主动调用
    autoPlay: true, // 是否自动播放
)

使用二进制数据

PAGView.bytes(
    "xxxx", // 网络链接
    repeatCount: PagView.REPEAT_COUNT_LOOP, // 循环次数
    initProgress: 0.25, // 初始进度
    key: pagKey,  // 利用key进行主动调用
    autoPlay: true, // 是否自动播放
)

在PAGView中加入回调参数

以下回调与原生PAG监听对齐:

PAGView.asset(
    ...
    onAnimationStart: (){  // 开始
      // do something
    },
    onAnimationEnd: (){   // 结束
      // do something
    },
    onAnimationRepeat: (){ // 重复
      // do something
    },
    onAnimationCancel: (){ // 取消
      // do something
    },

通过key获取state进行主动调用

final GlobalKey<PAGViewState> pagKey = GlobalKey<PAGViewState>();

// 传入key值
PAGView.url(key: pagKey)

// 播放
pagKey.currentState?.start();

// 暂停
pagKey.currentState?.pause();  

// 停止
pagKey.currentState?.stop();  

// 设置进度
pagKey.currentState?.setProgress(xxx);

// 获取坐标位置的图层名list
pagKey.currentState?.getLayersUnderPoint(x,y);

完整示例Demo

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:pag/pag.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    /// use codes below to config cache / multi-thread settings
    // PAG.enableCache(false);
    // PAG.enableMultiThread(false);
    // PAG.setCacheSize(4);   ///default size is 8
    return MaterialApp(
      home: MyHome(),
    );
  }
}

class MyHome extends StatefulWidget {
  [@override](/user/override)
  _MyHomeState createState() => _MyHomeState();
}

/// PAG用于ListView,用于测试加载速度
class _MyListHomeState extends State<MyHome> {
  bool visible = false;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextButton(
          child: Container(
            width: 100,
            height: 50,
            alignment: Alignment.center,
            color: Color.fromARGB(255, 100, 255, 100),
            child: Text('Test'),
          ),
          onPressed: () {
            setState(() {
              visible = !visible;
            });
          },
        ),
        Expanded(
            child: Visibility(
          visible: visible,
          child: ListView.builder(
              itemCount: 300,
              itemBuilder: (context, index) {
                return Row(
                  mainAxisSize: MainAxisSize.min,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    PAGView.asset(
                      'data/${index % 20}.pag',
                      width: (index % 7) * 20 + 10,
                      height: (index % 7) * 20 + 10,
                      repeatCount: PAGView.REPEAT_COUNT_LOOP,
                      initProgress: 0.25,
                      autoPlay: true,
                      key: ValueKey(index),
                    ),
                  ],
                );
              }),
        )),
      ],
    );
  }
}

class _MyHomeState extends State<MyHome> {
  GlobalKey<PAGViewState> _fansDanceKey = GlobalKey<PAGViewState>(debugLabel: _assetFans);
  GlobalKey<PAGViewState> _assetDanceKey = GlobalKey<PAGViewState>(debugLabel: _assetFans);
  GlobalKey<PAGViewState> get assetPagKey => _pagAsset == _assetFans ? _fansDanceKey : _assetDanceKey;

  final GlobalKey<PAGViewState> networkPagKey = GlobalKey<PAGViewState>();
  final GlobalKey<PAGViewState> bytesPagKey = GlobalKey<PAGViewState>();

  Uint8List? bytesData;

  // 本地加载资源
  static const String _assetFans = 'data/fans.pag';
  static const String _assetDance = 'data/dance.pag';
  static const String _assetError = 'data/error.pag';
  String _pagAsset = _assetFans;

  void changeAsset() {
    setState(() {
      _pagAsset = _pagAsset == _assetFans ? _assetDance : _assetFans;
    });
  }

  [@override](/user/override)
  void initState() {
    rootBundle.load("data/fans.pag").then((data) {
      setState(() {
        bytesData = Uint8List.view(data.buffer);
      });
    });
    super.initState();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('PAGView example app'),
        ),
        body: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              /// PAGView加载本地资源
              Padding(
                padding: EdgeInsets.only(top: 20, left: 12, bottom: 20),
                child: Text(
                  "PAGView加载本地资源:",
                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Colors.black54),
                ),
              ),
              SizedBox(
                width: 100,
                height: 100,
                child: PAGView.asset(
                  _pagAsset,
                  repeatCount: PAGView.REPEAT_COUNT_LOOP,
                  initProgress: 0.25,
                  autoPlay: true,
                  key: assetPagKey,
                ),
              ),
              Padding(
                padding: EdgeInsets.only(left: 12, top: 10),
                child: Row(
                  children: [
                    IconButton(
                      iconSize: 30,
                      icon: const Icon(
                        Icons.pause_circle,
                        color: Colors.black54,
                      ),
                      onPressed: () {
                        // 暂停
                        assetPagKey.currentState?.pause();
                      },
                    ),
                    IconButton(
                      iconSize: 30,
                      icon: const Icon(
                        Icons.play_circle,
                        color: Colors.black54,
                      ),
                      onPressed: () {
                        // 播放
                        assetPagKey.currentState?.start();
                      },
                    ),
                    IconButton(
                        iconSize: 30,
                        icon: const Icon(
                          Icons.published_with_changes_sharp,
                          color: Colors.black54,
                        ),
                        onPressed: changeAsset),
                    Text(
                      "<= 请点击控制动画(可切换)",
                      style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.black54),
                    ),
                  ],
                ),
              ),

              /// PAGView加载网络资源
              Padding(
                padding: EdgeInsets.only(top: 50, left: 12, bottom: 20),
                child: Text(
                  "PAGView加载网络资源:",
                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Colors.black54),
                ),
              ),
              PAGView.network(
                "https://svipwebwx-30096.sz.gfp.tencent-cloud.com/file1647585475981.pag",
                repeatCount: PAGView.REPEAT_COUNT_LOOP,
                initProgress: 0.25,
                autoPlay: true,
                key: networkPagKey,
              ),
              Padding(
                padding: EdgeInsets.only(left: 12, top: 10),
                child: Row(
                  children: [
                    IconButton(
                      iconSize: 30,
                      icon: const Icon(
                        Icons.pause_circle,
                        color: Colors.black54,
                      ),
                      onPressed: () {
                        // 暂停
                        networkPagKey.currentState?.pause();
                      },
                    ),
                    IconButton(
                      iconSize: 30,
                      icon: const Icon(
                        Icons.play_circle,
                        color: Colors.black54,
                      ),
                      onPressed: () {
                        // 播放
                        networkPagKey.currentState?.start();
                      },
                    ),
                    Text(
                      "<= 请点击控制动画",
                      style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.black54),
                    ),
                  ],
                ),
              ),

              /// PAGView加载二进制资源
              Padding(
                padding: EdgeInsets.only(top: 50, left: 12, bottom: 20),
                child: Text(
                  "PAGView加载二进制资源:",
                  style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: Colors.black54),
                ),
              ),
              Visibility(
                  visible: bytesData?.isNotEmpty == true,
                  child: PAGView.bytes(
                    bytesData,
                    repeatCount: PAGView.REPEAT_COUNT_LOOP,
                    initProgress: 0.25,
                    autoPlay: true,
                    key: bytesPagKey,
                  )),
              Padding(
                padding: EdgeInsets.only(left: 12, top: 10),
                child: Row(
                  children: [
                    IconButton(
                      iconSize: 30,
                      icon: const Icon(
                        Icons.pause_circle,
                        color: Colors.black54,
                      ),
                      onPressed: () {
                        // 暂停
                        bytesPagKey.currentState?.pause();
                      },
                    ),
                    IconButton(
                      iconSize: 30,
                      icon: const Icon(
                        Icons.play_circle,
                        color: Colors.black54,
                      ),
                      onPressed: () {
                        // 播放
                        bytesPagKey.currentState?.start();
                      },
                    ),
                    Text(
                      "<= 请点击控制动画",
                      style: TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Colors.black54),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ));
  }
}

更多关于Flutter分页插件pag的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter分页插件pag的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter中使用flutter_pagination插件来实现分页功能的示例代码。flutter_pagination是一个流行的分页插件,但需要注意的是,插件的具体名称和API可能会随时间变化,请确保查阅最新的文档和示例。

首先,确保你已经在pubspec.yaml文件中添加了flutter_pagination依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_pagination: ^x.y.z  # 替换为最新版本号

然后,运行flutter pub get来安装依赖。

接下来是一个简单的示例,展示如何使用flutter_pagination插件来实现分页加载数据列表:

import 'package:flutter/material.dart';
import 'package:flutter_pagination/flutter_pagination.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Pagination Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  PaginationController<int, MyItem> _paginationController;

  @override
  void initState() {
    super.initState();
    _paginationController = PaginationController<int, MyItem>(
      initialPage: 1,
      pageFetchSize: 10,
      itemBuilder: (context, item, index) {
        return ListTile(
          title: Text(item.title),
          subtitle: Text(item.description),
        );
      },
      onLoadMore: fetchPage,
    );
  }

  Future<void> fetchPage(int pageKey) async {
    try {
      final response = await http.get(
        Uri.parse('https://api.example.com/items?page=$pageKey'),
      );
      final List<MyItem> items =
          jsonDecode(response.body).map<MyItem>((json) => MyItem.fromJson(json)).toList();
      _paginationController.appendLastPageItems(items);
    } catch (error) {
      _paginationController.error = error;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Pagination Example'),
      ),
      body: PaginationView<int, MyItem>(
        controller: _paginationController,
        builderDelegate: PaginationBuilderDelegate<int, MyItem>(
          itemCountBuilder: (_, pageKey) => 10, // 每页的项目数
          loadMoreErrorBuilder: (_, error) => Center(child: Text('Error: $error')),
          emptyListBuilder: (_, pageKey) => Center(child: Text('No items')),
          loadingBuilder: (_, pageKey) => Center(child: CircularProgressIndicator()),
        ),
      ),
    );
  }

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

class MyItem {
  String title;
  String description;

  MyItem({required this.title, required this.description});

  factory MyItem.fromJson(Map<String, dynamic> json) {
    return MyItem(
      title: json['title'] as String,
      description: json['description'] as String,
    );
  }
}

在这个示例中:

  1. PaginationController用于管理分页逻辑,包括当前页码、每页的项目数、加载更多数据的函数等。
  2. fetchPage函数模拟了一个网络请求,它从API获取数据并将其转换为MyItem对象列表,然后调用appendLastPageItems方法将这些项目添加到列表中。
  3. PaginationView是呈现分页列表的组件,它使用PaginationBuilderDelegate来定义不同状态下的UI(如加载中、错误、空列表等)。

请确保将示例中的API URL替换为你自己的API端点,并根据你的API响应格式调整MyItem.fromJson方法。

这个示例提供了一个基础框架,你可以根据需要进行扩展和修改,比如添加下拉刷新、自定义加载指示器等。

回到顶部