GetX UI 基础组件
概述
本文档详细介绍了 FluCli 项目中基于 GetX 状态管理的基础 UI 组件,包括页面基类、列表页面基类和控制器基类的使用方法。通过这些基础组件,您可以快速构建具有统一风格和完整功能的 Flutter 页面。
基础页面组件
BaseGetXV 页面基类
BaseGetXV
是所有 GetX 页面的基类,它继承自 GetView
并混入了多个 Mixin,提供了完整的页面功能:
dart
// lib/common/base_ui/base_get_ui/base_getx_v.dart
abstract class BaseGetXV<T extends BaseGetXController> extends GetView<T>
with HzyScaffolMixin, HzyAppBarMixin, HzyBodyMixin, HzyAbsAttribute {
BaseGetXV({super.key});
@override
Widget build(BuildContext context) {
Widget body = createBuild(context: context);
return body;
}
@override
Widget createBuild({required BuildContext context}) {
return Obx(() => createScaffolWidget(context: context));
}
}
核心特性
- 响应式状态管理:使用
Obx
自动监听状态变化 - 统一页面结构:提供标准的 Scaffold、AppBar、Body 结构
- 生命周期管理:集成完整的页面生命周期
- 缺省页面支持:内置加载、空数据、错误页面
- 安全区域处理:自动处理刘海屏适配
使用示例
创建一个简单的 GetX 页面:
dart
// lib/pages/demo_page.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:your_project/common/base_ui/base_get_ui/base_getx_v.dart';
import 'package:your_project/vm/demo_controller.dart';
class DemoPage extends BaseGetXV<DemoController> {
DemoPage({Key? key}) : super(key: key);
@override
DemoController get controller => Get.put(DemoController());
@override
String getPageTitle() {
return '演示页面';
}
@override
Widget createBody({required BuildContext context, BoxConstraints? constraints}) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Obx(() => Text(
'计数: ${controller.count}',
style: Theme.of(context).textTheme.headlineMedium,
)),
const SizedBox(height: 20),
ElevatedButton(
onPressed: controller.increment,
child: const Text('增加'),
),
],
),
);
}
}
缺省页面配置
BaseGetXV
支持多种页面状态的缺省页面,您可以通过重写相应方法来自定义:
dart
class DemoPage extends BaseGetXV<DemoController> {
// 自定义空数据页面
@override
Widget? createEmptyWidget() {
return PlaceHoldPage(
placeHoldType: controller.placeHoldType,
msg: controller.placeMsg,
btnMsg: controller.placeBtnMsg,
onTap: () {
controller.tapPlaceHoldWidgetMethod(
placeHoldType: controller.placeHoldType,
);
},
);
}
// 自定义加载页面
@override
Widget? createLoadingWidget() {
return const Center(
child: CircularProgressIndicator(),
);
}
// 自定义错误页面
@override
Widget? createErrorWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.error, size: 64, color: Colors.red),
const SizedBox(height: 16),
const Text('加载失败'),
const SizedBox(height: 16),
ElevatedButton(
onPressed: controller.retry,
child: const Text('重试'),
),
],
),
);
}
}
页面配置选项
您可以通过重写以下方法来配置页面的各种属性:
dart
class DemoPage extends BaseGetXV<DemoController> {
// 页面标题
@override
String getPageTitle() => '我的页面';
// 是否显示导航栏
@override
bool get showAppBar => true;
// 是否显示返回按钮
@override
bool get showLeading => true;
// 背景颜色
@override
Color? get backgroundColor => Colors.white;
// 是否使用安全区域
@override
bool get useSafeArea => true;
// 导航栏操作按钮
@override
List<Widget>? get actions => [
IconButton(
icon: const Icon(Icons.settings),
onPressed: () => Get.toNamed('/settings'),
),
];
}
列表页面组件
BaseGetXlistV 列表基类
BaseGetXlistV
是专门为列表页面设计的基类,它在 BaseGetXV
的基础上集成了下拉刷新和上拉加载功能:
dart
// lib/common/base_ui/base_get_ui/base_getx_list_v.dart
abstract class BaseGetXlistV<T extends BaseGetXListController> extends BaseGetXV<T>
with HzyAbstracRefreshWidget {
BaseGetXlistV({super.key});
@override
Widget createBody({
required BuildContext context,
BoxConstraints? constraints,
}) {
return createRefreshWidget(context);
}
/// 创建列表刷新组件
@override
Widget createRefreshWidget(BuildContext context) {
return EasyRefresh(
controller: controller.refreshController,
onRefresh: () async {
controller.configRefresh();
},
onLoad: () async {
controller.configLoading();
},
header: createHeader(),
footer: createFooter(),
child: createRefreshChild(context),
);
}
/// 创建列表内容
Widget createRefreshChild(BuildContext context) {
return createListView(context);
}
/// 需要子类实现的列表视图
Widget createListView(BuildContext context);
}
列表页面使用示例
创建一个完整的列表页面:
dart
// lib/pages/demo_list_page.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:your_project/common/base_ui/base_get_ui/base_getx_list_v.dart';
import 'package:your_project/vm/demo_list_controller.dart';
class DemoListPage extends BaseGetXlistV<DemoListController> {
DemoListPage({Key? key}) : super(key: key);
@override
DemoListController get controller => Get.put(DemoListController());
@override
String getPageTitle() => '演示列表';
@override
Widget createListView(BuildContext context) {
return Obx(() {
if (controller.dataList.isEmpty) {
return const Center(
child: Text('暂无数据'),
);
}
return ListView.builder(
itemCount: controller.dataList.length,
itemBuilder: (context, index) {
final item = controller.dataList[index];
return ListTile(
title: Text(item.title),
subtitle: Text(item.subtitle),
onTap: () => controller.onItemTap(item),
);
},
);
});
}
}
列表状态管理
列表页面支持以下状态:
- 加载中:首次进入页面时显示
- 有数据:正常显示列表内容
- 空数据:没有数据时显示空状态页
- 加载更多:上拉时显示加载更多状态
- 没有更多:所有数据加载完成时显示
- 加载失败:网络请求失败时显示错误页面
自定义刷新组件
您可以自定义下拉刷新和上拉加载的样式:
dart
class CustomListPage extends BaseGetXlistV<CustomListController> {
@override
Header? createHeader() {
return const ClassicHeader(
dragText: '下拉刷新',
armedText: '释放刷新',
readyText: '正在刷新...',
processingText: '正在刷新...',
processedText: '刷新完成',
noMoreText: '没有更多',
failedText: '刷新失败',
messageText: '最后更新于 %T',
);
}
@override
Footer? createFooter() {
return controller.controlFinishLoad
? const ClassicFooter(
dragText: '上拉加载',
armedText: '释放加载',
readyText: '正在加载...',
processingText: '正在加载...',
processedText: '加载完成',
noMoreText: '没有更多',
failedText: '加载失败',
)
: const NotLoadFooter();
}
}
控制器组件
BaseGetXController 基础控制器
BaseGetXController
是所有 GetX 控制器的基类,它集成了网络请求、生命周期管理和页面状态管理功能:
dart
// lib/common/base_ui/base_get_ui/base_getx_controller.dart
abstract class BaseGetXController extends GetxController
with HzyAbstractNetWork, WidgetsBindingObserver, HzyNormalLifeCycleAbs {
/// 缺省页配置
final RxString placeMsg = ''.obs;
final RxString placeBtnMsg = ''.obs;
final Rx<PlaceHoldType> placeHoldType = PlaceHoldType.empty.obs;
/// 页面状态
final Rx<PageState> pageState = PageState.loading.obs;
/// 导航栏配置
final RxBool showAppBar = true.obs;
final RxString appBarTitle = ''.obs;
/// 安全区域配置
final RxBool useSafeArea = true.obs;
@override
void onInit() {
super.onInit();
WidgetsBinding.instance.addObserver(this);
initData();
}
@override
void onClose() {
WidgetsBinding.instance.removeObserver(this);
super.onClose();
}
/// 初始化数据
void initData() {
// 子类重写实现具体的初始化逻辑
}
/// 网络请求方法
Future<void> getNetWorkData() async {
// 子类重写实现具体的网络请求逻辑
}
/// 缺省页点击事件
void tapPlaceHoldWidgetMethod({required PlaceHoldType placeHoldType}) {
switch (placeHoldType) {
case PlaceHoldType.netWorkError:
case PlaceHoldType.empty:
getNetWorkData();
break;
default:
break;
}
}
}
控制器使用示例
创建一个简单的控制器:
dart
// lib/vm/demo_controller.dart
import 'package:get/get.dart';
import 'package:your_project/common/base_ui/base_get_ui/base_getx_controller.dart';
class DemoController extends BaseGetXController {
final RxInt count = 0.obs;
final RxString message = 'Hello World'.obs;
@override
void initData() {
super.initData();
// 初始化页面数据
loadData();
}
void increment() {
count.value++;
}
void updateMessage(String newMessage) {
message.value = newMessage;
}
@override
Future<void> getNetWorkData() async {
try {
pageState.value = PageState.loading;
// 模拟网络请求
await Future.delayed(const Duration(seconds: 2));
// 请求成功
pageState.value = PageState.success;
message.value = '数据加载成功';
} catch (e) {
// 请求失败
pageState.value = PageState.error;
placeHoldType.value = PlaceHoldType.netWorkError;
placeMsg.value = '网络请求失败';
placeBtnMsg.value = '重试';
}
}
void loadData() {
getNetWorkData();
}
void retry() {
getNetWorkData();
}
}
BaseGetXListController 列表控制器
专门为列表页面设计的控制器基类:
dart
// lib/common/base_ui/base_get_ui/base_getx_list_controller.dart
abstract class BaseGetXListController<T> extends BaseGetXController {
/// 刷新控制器
late EasyRefreshController refreshController;
/// 数据列表
final RxList<T> dataList = <T>[].obs;
/// 分页参数
final RxInt currentPage = 1.obs;
final RxInt pageSize = 20.obs;
final RxBool hasMore = true.obs;
final RxBool controlFinishLoad = true.obs;
@override
void onInit() {
super.onInit();
refreshController = EasyRefreshController(
controlFinishRefresh: true,
controlFinishLoad: controlFinishLoad.value,
);
}
@override
void onClose() {
refreshController.dispose();
super.onClose();
}
/// 下拉刷新
Future<void> configRefresh() async {
currentPage.value = 1;
hasMore.value = true;
await getNetWorkData();
refreshController.finishRefresh();
}
/// 上拉加载
Future<void> configLoading() async {
if (!hasMore.value) {
refreshController.finishLoad(IndicatorResult.noMore);
return;
}
currentPage.value++;
await getNetWorkData();
if (hasMore.value) {
refreshController.finishLoad(IndicatorResult.success);
} else {
refreshController.finishLoad(IndicatorResult.noMore);
}
}
/// 处理列表数据
void handleListData(List<T> newData, {bool isRefresh = false}) {
if (isRefresh) {
dataList.clear();
}
dataList.addAll(newData);
// 判断是否还有更多数据
hasMore.value = newData.length >= pageSize.value;
// 更新页面状态
if (dataList.isEmpty) {
pageState.value = PageState.empty;
placeHoldType.value = PlaceHoldType.empty;
placeMsg.value = '暂无数据';
} else {
pageState.value = PageState.success;
}
}
}
列表控制器使用示例
dart
// lib/vm/demo_list_controller.dart
import 'package:get/get.dart';
import 'package:your_project/common/base_ui/base_get_ui/base_getx_list_controller.dart';
import 'package:your_project/models/demo_model.dart';
import 'package:your_project/services/api_service.dart';
class DemoListController extends BaseGetXListController<DemoModel> {
final ApiService _apiService = Get.find<ApiService>();
@override
Future<void> getNetWorkData() async {
try {
if (currentPage.value == 1) {
pageState.value = PageState.loading;
}
final response = await _apiService.getDemoList(
page: currentPage.value,
pageSize: pageSize.value,
);
handleListData(
response.data,
isRefresh: currentPage.value == 1,
);
} catch (e) {
if (currentPage.value == 1) {
pageState.value = PageState.error;
placeHoldType.value = PlaceHoldType.netWorkError;
placeMsg.value = '加载失败';
placeBtnMsg.value = '重试';
}
}
}
void onItemTap(DemoModel item) {
Get.toNamed('/detail', arguments: item);
}
}
最佳实践
1. 状态管理建议
- 使用 Obx 进行局部刷新:只在需要响应状态变化的 Widget 上使用
Obx
- 合理使用 GetBuilder:对于复杂页面,可以使用
GetBuilder
进行更精确的控制 - 避免过度使用响应式变量:不是所有变量都需要是响应式的
2. 生命周期管理
dart
class MyController extends BaseGetXController {
@override
void onInit() {
super.onInit();
// 页面初始化时执行
print('页面初始化');
}
@override
void onReady() {
super.onReady();
// 页面准备完成时执行
print('页面准备完成');
}
@override
void onClose() {
// 页面销毁时执行清理工作
print('页面销毁');
super.onClose();
}
}
3. 错误处理
dart
class MyController extends BaseGetXController {
@override
Future<void> getNetWorkData() async {
try {
pageState.value = PageState.loading;
final result = await apiService.getData();
if (result.isSuccess) {
pageState.value = PageState.success;
// 处理成功数据
} else {
_handleError(result.message);
}
} catch (e) {
_handleError(e.toString());
}
}
void _handleError(String message) {
pageState.value = PageState.error;
placeHoldType.value = PlaceHoldType.netWorkError;
placeMsg.value = message;
placeBtnMsg.value = '重试';
}
}
快速开始
使用 FluCli
提供的dart 代码块
快速创建 GetX 页面:
代码片段 | 描述 | 使用场景 |
---|---|---|
glvc | 自带上拉加载和下拉刷新功能的列表界面和控制器 | 需要分页加载数据的完整页面 |
gvc | 自带生命周期管理的有状态界面和控制器 | 需要管理状态的完整页面 |
glv | 自带上拉加载和下拉刷新功能的列表界面 | 需要分页加载数据的完整页面 |
gv | 自带生命周期管理的有状态界面 | 需要管理状态的完整页面 |
glc | 自带上拉加载和下拉刷新功能的控制器 | 列表控制器 |
gc | 自带生命周期管理的有状态控制器 | 状态控制器 |
提示
更多代码块使用方法,请参考 Dart 代码片段速查手册