peter_package_demo 0.0.4
peter_package_demo: ^0.0.4 copied to clipboard
A new Flutter project.
example/main.dart
import 'package:flutter/material.dart';
import 'package:peter_package_demo/peter_package_demo.dart' as peter;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const FriendsPage(),
);
}
}
class _FriendCell extends StatelessWidget {
final String? imageUrl; //网络图片
final String? name;
final String? groupTitle;
final String? imageAssets; //本地图片
_FriendCell({
this.imageUrl,
this.name,
this.groupTitle,
this.imageAssets
}); //私有的构造方法(文件内部可访问)
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
alignment: Alignment.centerLeft,
//上下居中,左对齐
margin: const EdgeInsets.only(left: 10),
height: groupTitle != null ? 30 : 0,
color: peter.weChatThemeColor,
child: groupTitle != null
? Text(
groupTitle!,
style: TextStyle(color: Colors.grey),
)
: null,
),
Container(
color: Colors.white,
child: Row(
children: [
Container(
margin: EdgeInsets.all(10),
width: 34,
height: 34,
decoration: BoxDecoration(
//装饰器
borderRadius: BorderRadius.circular(5.0),
// image: imageUrl != null ? DecorationImage(image: NetworkImage(imageUrl!)) : DecorationImage(image: AssetImage(imageAssets!)),
image: DecorationImage(
image: imageUrl != null
? NetworkImage(imageUrl!) as ImageProvider
: AssetImage(imageAssets!,package: 'peter_package_demo')), //两种写法都可以 - 强制类型转换
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
//可以设置交叉轴(默认是center)
children: [
Container(
// alignment: Alignment.centerLeft, //也可以通过设置这个属性
height: 54,
child: Text(
name!,
style: const TextStyle(fontSize: 18),
),
),
Container(
color: peter.weChatThemeColor,
width: peter.screenWidth(context) - 54,
height: 0.5,
// margin: const EdgeInsets.only(top: 10),
),
//可以通过设置height:0 隐藏线条
],
),
],
),
),
],
);
}
}
class FriendsPage extends StatefulWidget {
//整个页面定义为Stateful并不好,因为整个数据都会保留(正常情况下要拆分)
const FriendsPage({Key? key}) : super(key: key);
@override
_FriendsPageState createState() => _FriendsPageState();
}
class _FriendsPageState extends State<FriendsPage> with AutomaticKeepAliveClientMixin {
//字典 - 存放每一个索引所对应要滚动的距离(距离需要根据实际情况计算)
Map _groupOffsetMap = {
peter.INDEX_WORDS[0]: 0.0, //🔍
peter.INDEX_WORDS[1]: 0.0, //⭐️
};
final double _cellHeight = 54.5; //cell的高度
final double _groupHeight = 30.0; //group头部高度
ScrollController? _scrollController; //ListView滚动需要用到
final List<Widget> words = [];
final List<Friends> _headerData = [
Friends(imageAssets: 'images/新的朋友.png', name: '新的朋友'),
Friends(imageAssets: 'images/群聊.png', name: '群聊'),
Friends(imageAssets: 'images/标签.png', name: '标签'),
Friends(imageAssets: 'images/公众号.png', name: '公众号'),
];
final List<Friends> _listData = [];
@override
bool get wantKeepAlive => true;
@override
void initState() {
//生命周期方法,页面被渲染(载入)的时候会进来 (需要重新运行) - 从通讯录切换到发现页面,通讯录页面会被销毁
super.initState();
_listData.addAll(
datas); //添加一个数组的数据到数组中 可以用:_listData..addAll(datas)..addAll(datas);
_listData.addAll(datas);
_listData.sort((Friends a, Friends b) {
//排序
return a.indexLetter!.compareTo(b.indexLetter!);
});
for (int i = 0; i < peter.INDEX_WORDS.length; i++) {
words.add(Expanded(
child: Text(
peter.INDEX_WORDS[i],
style: TextStyle(fontSize: 12, color: Colors.grey),
)) //words里面装的是组件(如果不用Expanded,占不满)
);
}
//在这里计算每个索引所对应的位置
var _groupOffset = _cellHeight * _headerData.length;
for (int i = 0; i < _listData.length; i++) {
if (i == 0) {
//第一个cell
_groupOffsetMap
.addAll({_listData[i].indexLetter!: _groupOffset}); //添加数据
_groupOffset += _cellHeight + _groupHeight; //更新值
} else if (_listData[i].indexLetter! == _listData[i - 1].indexLetter!) {
_groupOffset += _cellHeight; //只需更新数据
} else {
_groupOffsetMap
.addAll({_listData[i].indexLetter!: _groupOffset}); //添加数据
_groupOffset += _cellHeight + _groupHeight;
}
}
_scrollController = ScrollController();
}
Widget _itemForRow(BuildContext context, int index) {
//显示头部的4个cell
if (index < _headerData.length) {
return _FriendCell(
imageAssets: _headerData[index].imageAssets!,
name: _headerData[index].name!);
}
//其它cell
bool _needHideTitle = (index - 4 > 0 &&
_listData[index - 4].indexLetter ==
_listData[index - 5].indexLetter); //是否应该显示groupTitle
return _FriendCell(
imageUrl: _listData[index - 4].imageUrl!,
name: _listData[index - 4].name!,
groupTitle: _needHideTitle ? null : _listData[index - 4].indexLetter!,
);
}
@override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
appBar: AppBar(
backgroundColor: peter.weChatThemeColor, //全局定义的变量
title: Text('通讯录页面'),
centerTitle:
true, //如果导航栏上设置了多个按钮,title会往左偏移,这里设置为true即可居中 (怎么设置title左边的按钮?)
),
body: Stack(
//索引条要悬浮在ListView上面
children: [
//Flutter中有专门的弹框视图
Container(
color: peter.weChatThemeColor,
child: ListView.builder(
controller: _scrollController, //scroll滚动需要用到这个controller
itemBuilder: _itemForRow,
itemCount: _listData.length + _headerData.length,
)),
peter.IndexBar(
indexBarCallBack: (String str) {
//回调
if (_groupOffsetMap[str] != null) {
_scrollController!.animateTo(_groupOffsetMap[str],
duration: Duration(microseconds: 100),
curve: Curves.easeIn); //滚动(距离、时间、动画)
}
},
),
],
),
);
}
}
class Friends{
final String? imageAssets; //本地图片
final String? imageUrl; //网络图像
final String? name; //姓名
final String? indexLetter; //首字母
Friends({this.imageUrl,this.name,this.indexLetter,this.imageAssets});
}
List <Friends> datas = [
Friends(
imageUrl: 'https://randomuser.me/api/portraits/women/27.jpg',
name: 'Lina',
indexLetter: 'L'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/women/17.jpg',
name: '菲儿',
indexLetter: 'F'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/women/16.jpg',
name: '安莉',
indexLetter: 'A'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/men/31.jpg',
name: '阿贵',
indexLetter: 'A'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/women/22.jpg',
name: '贝拉',
indexLetter: 'B'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/women/37.jpg',
name: 'Lina',
indexLetter: 'L'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/women/18.jpg',
name: 'Nancy',
indexLetter: 'N'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/men/47.jpg',
name: '扣扣',
indexLetter: 'K'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/men/3.jpg',
name: 'Jack',
indexLetter: 'J'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/women/5.jpg',
name: 'Emma',
indexLetter: 'E'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/women/24.jpg',
name: 'Abby',
indexLetter: 'A'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/men/15.jpg',
name: 'Betty',
indexLetter: 'B'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/men/13.jpg',
name: 'Tony',
indexLetter: 'T'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/men/26.jpg',
name: 'Jerry',
indexLetter: 'J'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/men/36.jpg',
name: 'Colin',
indexLetter: 'C'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/women/12.jpg',
name: 'Haha',
indexLetter: 'H'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/women/11.jpg',
name: 'Ketty',
indexLetter: 'K'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/women/13.jpg',
name: 'Lina',
indexLetter: 'L'
),
Friends(
imageUrl: 'https://randomuser.me/api/portraits/women/23.jpg',
name: 'Lina',
indexLetter: 'L'
),
];