基于GetX 搭建通用flutter 项目《三》(暗黑模式)
基于GetX 搭建通用flutter 项目《五》(基于GetX 进行动态刷新)
最近由于一直在写小程序开发,也是没有太多的时间,就耽误了这个文章的更新,实在是不好意思,好了,废话不多说了,直接上主题
您能在这里看到啥
- 抽象类
- 通用属性抽象类
- 通用界面抽象类
- 通用列表页抽象类
- 通用列表页刷新事件抽象类
- 网络事件抽象类
这些抽象类,是在自己的项目中,不断试错,自己总结的一些让自己偷懒的方法,一直以来,我对自己的写代码的要求就是,能“偷懒“就“偷懒“,减少代码中重复且可以抽象的方法的出现。喜欢折腾,会把之前写过的东西,不断的换着不同的方式,方法,去重写,在不断的重写中,慢慢的积累,不断的提升自己的思维能力,让自己变得更“懒”。
送给自己一句话
慢就是快
抽象类介绍
通用属性抽象类
abstract class AbstractAttribute {
/// 导航栏标题
final String? title = null;
/// 导航栏颜色
final Color? navbackcolor = null;
/// 默认安全区顶部 忽略
final bool safeAreatop = true;
/// 默认安全区底部 忽略
final bool safeAreabottm = true;
/// 网络加载状态
final PageState pageState = PageState.initializedState;
/// 背景颜色
final Color? backgroundColor = null;
/// 滚动属性
final bool? resizeToAvoidBottomInset = null;
}
从代码里,我们会发现,这里面只有一个属性非系统自带的,那就是PageState,其实这是我自己定义的界面显示的状态值,这里面包含了网络请求的状态值,以及界面初始化,加载完成等等,往👇看
enum PageState {
// 初始状态
initializedState,
// 错误状态,显示失败界面
errorState,
// 错误状态,只弹错误信息
erroronlyTotal,
// 错误状态,显示刷新按钮
errorshowRelesh,
// 没有更多数据
noMoreDataState,
// 空数据状态
emptyDataState,
// 数据获取成功状态
dataFetchState,
}
相对来说,这是我一般项目中,界面的一般状态值,这是我自己的总结,不代表,只能这样写,这个是需要根据您的业务场景,自己去归纳总结,灵活使用就行,主要是要学会,把界面通用的事件,和状态,抽离出来。方便使用者使用,且要简单易懂。不能只能你自己知道,这样的抽离,是没有意义。万物归一,简单至上。具体这个属性和状态值的是使用,我会在基于GetX 搭建通用flutter 项目《五》,这边文章里,详细介绍。
当然,有了属性,必然会有使用的地方,下面我们就来看看这些属性,到底能做些什么呢,快看界面抽象类
通用界面抽象类
这里会相对来说多一些,请您慢慢看,上代码👇
// 普通界面 规范
abstract class AbstractWidget {
/// 创建scaffoll
Widget createScaffol({
required BuildContext context,
required bool safeAreatop,
required bool safeAreabottm,
required PageState pageState,
required String? title,
required Color? backgroundColor,
required Color? navbackcolor,
required bool? resizeToAvoidBottomInset,
}) {
return Scaffold(
resizeToAvoidBottomInset: resizeToAvoidBottomInset,
backgroundColor: backgroundColor,
appBar: createAppBar(
context,
title: title,
navbackcolor: navbackcolor,
),
body: createSafeArea(
context,
safeAreatop,
safeAreabottm,
pageState,
),
);
}
/// 创建safe
Widget createSafeArea(
BuildContext context,
bool safeAreatop,
bool safeAreabottm,
PageState pageState,
) {
return SafeArea(
child: createCommBaseWidget(
context,
pageState,
),
top: safeAreatop,
bottom: safeAreabottm,
);
}
/// 创建导航栏
PreferredSize? createAppBar(
BuildContext context, {
Color? navbackcolor,
String? title,
}) {
if (title != null) {
return AppBarGenerator.getNoramlAppBar(
context: context,
title: title,
backgroundColor: navbackcolor,
actions: createAppBaractions(),
leading: createAppBarleading(),
textColor: createAppBarTextColor(),
leadingIconColor: createLeadingIconColor(),
leadingCallback: () {
configleadingCallbak(context);
},
titlew: createAppBarTitleWidget()
);
}
return null;
}
/// 创建AppBar titleWidget
Widget? createAppBarTitleWidget() {
return null;
}
/// 设置系统自带的返回按钮颜色
Color? createLeadingIconColor() {
return null;
}
/// 设置AppBar text 字体颜色
Color? createAppBarTextColor() {
return null;
}
/// 创建导航栏 右边按钮集合
List<Widget>? createAppBaractions() {
return null;
}
/// 重写返回按钮控件
Widget? createAppBarleading() {
return null;
}
/// 创建通用站位界面
Widget createCommBaseWidget(
BuildContext context,
PageState pageState,
) {
/// CommonBasePage 是我自己定义的通用界面暂位页。
return CommonBasePage(
pageState: pageState,
child: createColumWidget(context),
errorWidget: createErrorWidget(),
);
}
/// 创建界面Colum
Widget createColumWidget(BuildContext context) {
return Column(
children: [
createHeaderWidget(),
Expanded(
child: createBody(context),
),
],
);
}
/// 创建头部
Widget createHeaderWidget() {
return Container();
}
/// 创建真实body
@protected
Widget createBody(BuildContext context);
/// 创建失败 界面
Widget? createErrorWidget() {
return null;
}
/// 点击通用返回按钮点击事件
configleadingCallbak(BuildContext context) {
configgoback(context);
}
/// 执行返回界面
configgoback(BuildContext context) {
Navigator.of(context).pop();
}
}
-
Widget createScaffol() 创建界面入口方法
下面是函数需要的参数
/// 上下文,这就不再讲了
required BuildContext context,
/// 是否关闭顶部安全区域,这是当你需要沉浸式,和顶部置顶的时候,需要设置
required bool safeAreatop,
/// 是否关闭底部安全取悦,一般我都用于列表类界面。
required bool safeAreabottm,
/// 界面状态值
required PageState pageState,
/// 导航栏标题。我这边做的处理是,当title == null 就没有导航栏
required String? title,
/// 背景颜色
required Color? backgroundColor,
/// 导航栏背景颜色
required Color? navbackcolor,
/// 键盘启动 界面弹性 配置
required bool? resizeToAvoidBottomInset,
-
PreferredSize? createAppBar() 创建导航栏
从👆的代码里,我们很快就找到了创建导航栏 的方法,为了方便在使用的时候,更好的自定义导航栏,我在自己的使用过程中,增加了几个可以自定义导航栏的方法,👇,当然,如果你不喜欢我定义的,您也可以直接重写这个方法。来到达您的预期。/// 自定义默认返回Icon 的颜色,我这里默认用的 Icons.arrow_back_ios - Color? createLeadingIconColor() /// 自定义导航栏字体颜色 - Color? createAppBarTextColor() /// 创建导航栏👉按钮集合 - List<Widget>? createAppBaractions() /// 重写返回按钮控价 Widget? createAppBarleading()
其实这些方法,也是可以在通用属性抽象类 添加属性,由于这里为了体现两种替换方式,我这边,把只属于appBar的部分自定义属性,以重写方法的形式,来达到替换默认属性的效果,当然在重写通用属性抽象类中的属性的时候,我推荐👇的方式来重写
/// 第一种方式,直接重写get 方法,返回默认值 @override Color? get backgroundColor => Colors.white; /// 第二种方式,也是重写get 方法,但获取的可能是动态变更的,两种方式 /// 都是一样效果,不过是不是觉得第二种。和上面的定义抽象接口,再重写, /// 其实是一样的 /// 不管哪种方式,都只是一种方式,最终是我们形成体系,大家都按照我们规定好的方式 /// 来进行开发。节省沟通成本 @override Color? get navbackcolor => configbackColor(); Color? configbackColor() { return null; }
Widget createSafeArea() 创建安全区域body
这里面主要做了一件事,就是我们可视界面都被SafeArea 包裹着,然后我们的child的载体就是我们封装的展位界面,这个暂位界面会根据,你设定的界面状态值pagestate 进行了逻辑处理,到底界面是显示我们的空数据界面,网络请求失败,还是业务界面,
/// 创建通用站位界面
/// 这里面就需要您来传递,界面的状态值,
/// 有了状态值,站位界面,才能根据事先约定好的状态,来完成界面显示
Widget createCommBaseWidget(
BuildContext context,
/// 界面状态,可以查看👆
/// 我的状态值的定义
PageState pageState,
) {
/// 这里就是我封装的 站位界面的 工具
return CommonBasePage(
pageState: pageState,
child: createColumWidget(context),
/// 为了方便扩展,我在这里对外公开了一个方法
/// 用于创建你自己的失败界面.
errorWidget: createErrorWidget(),
);
}
- Widget createColumWidget(BuildContext context) 创建界面分成
/// 创建界面Colum
Widget createColumWidget(BuildContext context) {
return Column(
children: [
createHeaderWidget(),
Expanded(
child: createBody(context),
),
],
);
}
我这里用的是column,因为大部分界面可以分为
/// header 可以通过👆的方法,自定header
/// 一般这个用的不多
header (头部)
/// 这里才是我们真正内容的显示
/// 在这里,我一般是需要使用者,必须实现这个方法的
body(内容)
好了到这里,这个通用的界面抽象类暂时完成了,由于我们的项目开发中还有有很多组合,我这里也把通用列表,弄了一个简单抽象类请看👇
通用列表抽象类
/// 刷新界面 规范
abstract class AbstracRefreshWidget {
/// 创建刷新控件
Widget createRefreshWidget(BuildContext context);
/// 创建列表
Widget createListView(BuildContext context);
/// 创建列表 item
Widget createListitem(BuildContext context, int index);
/// 创建缺省页界面
Widget? createEmptyWidget();
}
相对来说,这里只是抽象了界面的方法,并没有包含事件的方法,我这么做的目的,是为了让我们的抽象类,
更纯粹一些,界面就是界面,事件就是事件.也方便使用MVC.或者MVVM,既然有了界面抽象类,自然也少不了
我们的方法抽象类,请往下看
通用列表刷新事件抽象类
/// 刷新界面 触发方法
abstract class AbstracRefreshMehod {
int page = 1;
/// 结束刷新
void endRefresh(int type, PageState pageState);
/// 下啦刷新 触发事件
void configRefresh();
/// 上啦加载 触发事件
void configLoading();
}
这里面的代码就相对简单了,就不再啰嗦了,当然下面的网络,我也就不再介绍了,相对来说比较简单
网络事件抽象类
// 配置网络请求规范
abstract class AbstractNetWork {
@protected
/// 通用网络参数
Map<String, dynamic>? configNetWorkParmas({int? type});
/// 网络请求
@protected
void getnetworkdata(int? type, Map<String, dynamic>? info);
}
具体使用事例
state 使用事例
第一步,创建适用你项目的抽象类.主要就是把我们上面定义的抽象类,进行一个组合.
StatefulWidget的抽象类
/// 其实这个到没有太有必要做成抽象类.
abstract class NormalStatefulWidget extends StatefulWidget {
const TTNormalStatefulWidget({Key? key}) : super(key: key);
@override
// ignore: no_logic_in_create_state
TTNormalState createState() => getState();
TTNormalState getState();
}
State 抽象类
abstract class NormalState<T extends StatefulWidget> extends State<T>
with AbstractNetWork, AbstractWidget, AbstractAttribute {
/// 生命周期
///
///
/// 界面初始化完成
@override
void initState() {
initDefaultState();
super.initState();
}
/// 界面构建视图入口
@override
Widget build(BuildContext context) {
return createScaffol(
context: context,
safeAreatop: safeAreatop,
safeAreabottm: safeAreabottm,
pageState: pageState,
resizeToAvoidBottomInset: resizeToAvoidBottomInset,
title: title,
backgroundColor: backgroundColor,
navbackcolor: navbackcolor,
);
}
@override
void dispose() {
initDefaultDispose();
super.dispose();
}
@override
Widget createColumWidget(BuildContext context) {
return Container(
alignment: Alignment.center,
child: LayoutBuilder(
builder: (context, constraints) {
configlayoutbuiderConstraints(constraints);
return SizedBox(
width: configSizeBoxWidth(constraints),
child: createonlyColumWidget(context));
},
),
);
}
/// getx 真实包裹的colum 方法
Widget createonlyColumWidget(BuildContext context) {
return Column(
children: [
createHeaderWidget(),
Expanded(
child: createBody(context),
),
],
);
}
/// 网络请求
///
///
@override
void getnetworkdata(int? type, Map<String, dynamic>? info) {}
/// 创建视图
///
///
/// 触发方法
///
///
@override
Map<String, dynamic>? configNetWorkParmas() {
return null;
}
/// 界面进入
void initDefaultState() {}
/// 界面销毁
void initDefaultDispose() {}
/// 通用刷新state
void configsetState(VoidCallback fn) {
if (mounted) {
setState(() {
fn();
});
}
}
/// 获取 屏幕 最大尺寸
configlayoutbuiderConstraints(BoxConstraints constraints) {}
double? configSizeBoxWidth(BoxConstraints constraints) {
return HzyNormalUtils.configSizeMaxW(constraints.maxWidth);
}
}
好了,今天就写到这里了,喜欢的可以点个赞
对应的基于GetX 封装的将会在
[基于GetX 搭建通用flutter 项目《五》()这个文章里讲解
hzy_normal_widget 是我在使用GetX搭建项目时,总结的一些通用开发控件,方便我们在开发的时候,减少重复性界面代码的创建.
ttcomment 通用项目的界面接口基类,和一些通用工具类,喜欢的可以点点star.