Flutter插件crab_test_package_001的介绍与使用
Flutter插件crab_test_package_001的介绍与使用
本文将探讨Flutter插件crab_test_package_001
的功能及其潜在用途。该插件主要用于构建一个类似于通讯录的应用界面,支持快速滚动浏览联系人列表,并通过右侧字母索引实现便捷导航。
功能概述 #
此插件的核心功能包括:
- 支持快速滚动浏览联系人列表。
- 右侧提供字母索引,用于快速定位联系人分组。
- 可自定义联系人头像及姓名显示样式。
- 支持动态加载数据并更新UI。
使用前准备 #
要使用该插件,首先确保您的项目已配置好Flutter环境。然后在pubspec.yaml
文件中添加依赖项:
flutter pub get
以安装插件。
使用示例 #
以下是一个完整的示例代码,展示如何利用crab_test_package_001
构建通讯录页面。
import 'package:crab_test_package_001/crab_test_package_001.dart' as crab;
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: '通讯录Demo',
theme: ThemeData(
platform: TargetPlatform.iOS,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
primarySwatch: Colors.grey,
),
home: const FriendsPage(),
);
}
}
class FriendsPage extends StatefulWidget {
const FriendsPage({Key? key}) : super(key: key);
[@override](/user/override)
_FriendsPageState createState() => _FriendsPageState();
}
class _FriendsPageState extends State<FriendsPage>
with AutomaticKeepAliveClientMixin<FriendsPage> {
ScrollController? _controller;
Map<String, double>? _mapGroupHeight;
String _selectedWord = '';
double _bubbleY = 0.0;
bool _isBubbleHind = true;
[@override](/user/override)
bool get wantKeepAlive => true;
[@override](/user/override)
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
appBar: AppBar(
title: const Text('通讯录'),
backgroundColor: const Color.fromRGBO(240, 240, 240, 1.0),
elevation: 0.0,
centerTitle: true,
),
body: Container(
color: const Color.fromRGBO(240, 240, 240, 1.0),
child: Stack(
alignment: Alignment.centerRight,
children: [
FriendsTable(getListDataCallback: (ScrollController controller,
Map<String, double> mapGroupHeight) {
_controller = controller;
_mapGroupHeight = mapGroupHeight;
}),
SizedBox(
width: crab.FriendsBubble.ThisWidth + crab.FriendsWords.ThisWidth,
child: Row(
children: [
_getLeftView(),
_getRightView(),
],
),
),
],
),
),
);
}
Widget _getLeftView() {
return crab.FriendsBubble(
bubbleY: _bubbleY, word: _selectedWord, isBubbleHind: _isBubbleHind);
}
Widget _getRightView() {
return crab.FriendsWords(
dragSelectedCallback: (intIndex) {
if (_controller == null || _mapGroupHeight == null) {
return;
}
String strWord = crab.listWords[intIndex];
if (_selectedWord == strWord) {
return;
}
setState(() {
_selectedWord = strWord;
_bubbleY = 2.0 / (crab.listWords.length - 1) * intIndex - 1;
_isBubbleHind = false;
});
if (_mapGroupHeight![strWord] == null) {
return;
}
double dblGroupHeight = _mapGroupHeight![strWord] as double;
if (dblGroupHeight > _controller!.position.maxScrollExtent) {
dblGroupHeight = _controller!.position.maxScrollExtent;
}
_controller!.animateTo(dblGroupHeight,
duration: const Duration(milliseconds: 100), curve: Curves.easeIn);
},
dragUnselectedCallback: () {
setState(() {
_isBubbleHind = true;
});
},
);
}
}
typedef GetListDataCallback = void Function(
ScrollController controller, Map<String, double> mapGroupHeight);
class FriendsTable extends StatelessWidget {
final ScrollController _controller = ScrollController();
FriendsTable({GetListDataCallback? getListDataCallback}) {
listFriend.sort((a, b) {
return a.groupLetter!.compareTo(b.groupLetter!);
});
if (getListDataCallback != null) {
Map<String, double> map = _getCalculateGroupHeightMap();
getListDataCallback(_controller, map);
}
}
[@override](/user/override)
Widget build(BuildContext context) {
return Container(
child: ListView.builder(
controller: _controller,
itemBuilder: (BuildContext context, int index) {
FriendsInfo friendsInfo = listFriend[index];
bool isGroupHeader = true;
if (index > 0) {
FriendsInfo preInfo = listFriend[index - 1];
isGroupHeader =
friendsInfo.groupLetter == preInfo.groupLetter ? false : true;
}
bool isLine = false;
if (index < listFriend.length - 1) {
FriendsInfo nextInfo = listFriend[index + 1];
isLine =
friendsInfo.groupLetter == nextInfo.groupLetter ? true : false;
}
return FriendsCell(
name: friendsInfo.name,
networkImage: friendsInfo.imageUrl,
groupLetter: friendsInfo.groupLetter,
isLine: isLine,
isGroupHeader: isGroupHeader);
},
itemCount: listFriend.length,
),
);
}
Map<String, double> _getCalculateGroupHeightMap() {
Map<String, double> map = {};
double dblGroupHeight = 0;
for (int intIndex = 0; intIndex < listFriend.length; intIndex++) {
FriendsInfo friendsInfo = listFriend[intIndex];
if (intIndex == 0) {
map[friendsInfo.groupLetter!] = dblGroupHeight;
dblGroupHeight += FriendsCell.GroupHeight + FriendsCell.CellHeight;
continue;
}
FriendsInfo preInfo = listFriend[intIndex - 1];
if (friendsInfo.groupLetter! == preInfo.groupLetter!) {
dblGroupHeight += FriendsCell.CellHeight;
continue;
}
map[friendsInfo.groupLetter!] = dblGroupHeight;
dblGroupHeight += FriendsCell.GroupHeight + FriendsCell.CellHeight;
}
return map;
}
}
class FriendsCell extends StatefulWidget {
static const double CellHeight = 72;
static const double GroupHeight = 28;
final String? networkImage;
final String? assetImage;
final String name;
final String? groupLetter;
final bool isLine;
final bool isGroupHeader;
const FriendsCell({
this.networkImage,
this.assetImage,
required this.name,
this.groupLetter,
this.isLine = false,
this.isGroupHeader = false,
});
[@override](/user/override)
_FriendsCellState createState() => _FriendsCellState();
}
class _FriendsCellState extends State<FriendsCell> {
Color _cellColor = Colors.white;
[@override](/user/override)
Widget build(BuildContext context) {
return _getAssignCell();
}
Widget _getAssignCell() {
Widget cell = widget.isGroupHeader ? _getCellHaveGroup() : _getCell();
if (widget.isLine) {
return _getCellHaveLine(cell);
}
return cell;
}
Widget _getCellHaveGroup() {
return Column(
children: [
Container(
margin: const EdgeInsets.only(left: 10),
alignment: Alignment.centerLeft,
height: FriendsCell.GroupHeight,
child: Text(
widget.groupLetter!,
style: const TextStyle(
color: Colors.grey,
fontSize: 14,
),
),
),
_getCell(),
],
);
}
Widget _getCellHaveLine(Widget cell) {
return Stack(
children: [
cell,
Positioned(
bottom: 0,
right: 0,
left: 50,
child: Container(
height: 0.5,
color: const Color.fromRGBO(240, 240, 240, 1.0),
),
),
],
);
}
Widget _getCell() {
return GestureDetector(
onTap: () {
setState(() {
_cellColor = Colors.white;
});
},
onTapDown: (TapDownDetails details) {
setState(() {
_cellColor = const Color.fromRGBO(220, 220, 220, 1.0);
});
},
onTapCancel: () {
setState(() {
_cellColor = Colors.white;
});
},
child: Container(
color: _cellColor,
height: FriendsCell.CellHeight,
child: Row(
children: [
Container(
margin: const EdgeInsets.only(left: 16, right: 16),
height: 40,
child: widget.networkImage == null
? Image.asset(
widget.assetImage!,
)
: Image.network(
widget.networkImage!,
),
),
Text(
widget.name,
style: const TextStyle(
color: Colors.black,
fontSize: 20,
),
)
],
),
),
);
}
}
class FriendsInfo {
String imageUrl;
String name;
String? groupLetter;
FriendsInfo({
required this.imageUrl,
required this.name,
this.groupLetter,
});
}
List<FriendsInfo> listFriend = [
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/women/27.jpg',
name: 'Lina',
groupLetter: 'L'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/women/17.jpg',
name: '菲儿',
groupLetter: 'F'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/women/16.jpg',
name: '安莉',
groupLetter: 'A'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/men/31.jpg',
name: '阿贵',
groupLetter: 'A'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/women/22.jpg',
name: '贝拉',
groupLetter: 'B'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/women/37.jpg',
name: 'Lina',
groupLetter: 'L'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/women/18.jpg',
name: 'Nancy',
groupLetter: 'N'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/men/47.jpg',
name: '扣扣',
groupLetter: 'K'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/men/3.jpg',
name: 'Jack',
groupLetter: 'J'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/women/5.jpg',
name: 'Emma',
groupLetter: 'E'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/women/24.jpg',
name: 'Abby',
groupLetter: 'A'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/men/15.jpg',
name: 'Betty',
groupLetter: 'B'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/men/13.jpg',
name: 'Tony',
groupLetter: 'T'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/men/26.jpg',
name: 'Jerry',
groupLetter: 'J'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/men/36.jpg',
name: 'Colin',
groupLetter: 'C'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/women/12.jpg',
name: 'Haha',
groupLetter: 'H'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/women/11.jpg',
name: 'Ketty',
groupLetter: 'K'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/women/13.jpg',
name: 'SiSi',
groupLetter: 'S'),
FriendsInfo(
imageUrl: 'https://randomuser.me/api/portraits/women/23.jpg',
name: 'Zare',
groupLetter: 'Z'),
];