Flutter分页插件pag的使用
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
更多关于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,
);
}
}
在这个示例中:
PaginationController
用于管理分页逻辑,包括当前页码、每页的项目数、加载更多数据的函数等。fetchPage
函数模拟了一个网络请求,它从API获取数据并将其转换为MyItem
对象列表,然后调用appendLastPageItems
方法将这些项目添加到列表中。PaginationView
是呈现分页列表的组件,它使用PaginationBuilderDelegate
来定义不同状态下的UI(如加载中、错误、空列表等)。
请确保将示例中的API URL替换为你自己的API端点,并根据你的API响应格式调整MyItem.fromJson
方法。
这个示例提供了一个基础框架,你可以根据需要进行扩展和修改,比如添加下拉刷新、自定义加载指示器等。