每次仿制一份代码
看着重复的代码发愣
是时分拿起重构的武器了
前言
编写了一个PagingListWidget
组件,提取了分页列表通用逻辑,其他事务相关的选用虚办法,由子类来完结。
通用逻辑包括:(1)下拉刷新。(2)上拉加载。(3)空数据展现。
其他事务逻辑:(1)导航栏信息。(2)获取列表信息。(3)列表项的视图。
运用办法,直接承继PagingListWidget
并完结虚办法。完好代码和样例,请参看分页列表;
一、 起因
作为事务开发工程师,每次新需求来,看到都是列表展现,毫无技能难度。直接ctrl + c 然后ctrl + v,一顿接口修改,开发完结。
一直觉得这已经挺良心了。可忽然腻了。
想着是不是可以再懒一点,想多点自由时刻来研究怎么能够更懒。
二、 提取
暴力开启,直接复制了一个列表页面的代码,然后将事务相关代码删去,仅仅留下通用的逻辑代码。如下:
/// 分页列表的页面
/// navigationTitle回来标题,页面展现一个title,一个列表。
/// navigationTitle回来null。页面仅仅为一个列表。此时可以重载build添加其他widgets。
abstract class PagingListWidget extends StatefulWidget {
const PagingListWidget({Key? key}) : super(key: key);
@override
PagingListWidgetState createState();
}
abstract class PagingListWidgetState<T extends PagingListWidget, S> extends State<T> {
// 完结类 获取数据时运用
final int pageSize = 20;
int page = 1;
final List<S> dataList = [];
bool showLoadingMore = false;
int total = 0;
bool hasInitialed = false;
bool _disposed = false;
final ScrollController _scrollController = ScrollController();
final GlobalKey<RefreshIndicatorState> _refreshKey = GlobalKey();
@override
void initState() {
super.initState();
_scrollController.addListener(() {
if (showLoadingMore) {
return;
}
if (_scrollController.position.pixels > (_scrollController.position.maxScrollExtent - 20) && total > dataList.length) {
setState(() {
showLoadingMore = true;
});
_loadMore();
}
});
Future.delayed(const Duration(seconds: 0), () {
_onRefresh();
});
}
@override
void dispose() {
_disposed = true;
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
var title = navigationTitle();
if (null == title) {
return _contentWidget();
}
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: _contentWidget(),
);
}
// Widget __START__
Widget _contentWidget() {
return SafeArea(
child: !hasInitialed
? const MyCircularProgress()
: RefreshIndicator(
key: _refreshKey,
onRefresh: _onRefresh,
child: dataList.isEmpty
? const NoData()
: ListView.builder(
itemBuilder: (context, index) {
if (index == dataList.length) {
return showLoadingMore
? Container(
margin: const EdgeInsets.all(10),
child: const Align(
child: CircularProgressIndicator(
color: MyColors.mainColor,
),
),
)
: total <= dataList.length
? Container(
margin: const EdgeInsets.all(10),
alignment: Alignment.center,
child: const Text("没有更多数据"),
)
: const SizedBox(
height: 44,
);
}
return listItem(index);
},
physics: const AlwaysScrollableScrollPhysics(),
itemCount: dataList.length + 1,
controller: _scrollController,
),
),
);
}
// Widget __END__
// Network __START__
Future<void> _onRefresh() async {
dataList.clear();
page = 1;
if (_disposed) {
return;
}
fetchData();
}
Future<void> _loadMore() async {
page++;
if (_disposed) {
return;
}
fetchData();
}
// Network __END__
// 虚办法 __START__
// 标题
// 回来为null 则说明此页面不要包括导航栏,仅仅回来列表页面。
// 回来标题时,那么直接展现完好的页面
String? navigationTitle();
// 获取列表的数据。
Future<void> fetchData();
// 列表视图
Widget listItem(int index);
// 虚办法 __END__
}
三、 思路
1. 为了运用尽量便利,直接承继StatefulWidget
或ConsumerStatefulWidget
。
2. 将一切的值都放在父类,子类经过super
来访问。
3. 列表信息的data model,选用泛型传入。abstract class PagingListWidgetState<T extends PagingListWidget, S>
避免类型强转。
4. 提高复用性。依据navigationTitle
来判断是否是带导航栏。
5. 提供自定义的部分,选用虚办法,让子类来完结。
四、样例
将原来的页面选用PagingListWidget
重写下,代码量减少了一半。而且今后编写分页列表的新页面,开发时刻减少90%吧。爽了。
举一个比如,如下:
class MyList extends PagingListWidget {
const MyList({super.key});
@override
PagingListWidgetState<PagingListWidget, dynamic> createState() => _MyListState();
}
class _MyListState extends PagingListWidgetState<MyList, String> {
var random = Random();
@override
Future<void> fetchData() async {
return Future.delayed(const Duration(seconds: 3), () {
super.total = 100000;
super.dataList.addAll(List.generate(super.pageSize, (index) => "测试下 ${random.nextInt(10000000)}"));
setState(() {
super.showLoadingMore = false;
super.hasInitialed = true;
});
});
}
@override
Widget listItem(int index) {
return Container(
color: MyColors.randomColor(),
height: 44,
padding: const EdgeInsets.only(top: 8, left: 8),
child: Text(super.dataList[index], style: TextStyle(color: MyColors.randomColor(), fontSize: 16),),
);
}
@override
String? navigationTitle() {
return "测试分页列表";
}
}
1. 创建子类。
2. 完结虚办法。
3. 完结
五、结束
思路忽然打开了。重复的代码今后就用这个方法重构下,代码量和开发量下降好多。
抛砖引玉。请赐教更好的思路。