基础组件
概述
本文档详细介绍了 FluCli 项目中基于 Flutter 原生状态管理的基础 UI 组件,包括页面基类、列表页面基类的使用方法。通过这些基础组件,您可以快速构建具有统一风格和完整功能的 Flutter 页面,无需依赖第三方状态管理库。
基础页面组件
BaseStateV 页面基类
BaseStateV
是所有原生状态管理页面的基类,它继承自 State
并混入了多个 Mixin,提供了完整的页面功能:
dart
// lib/common/base_ui/base_normal_ui/base_state.dart
abstract class BaseStateV<T extends StatefulWidget> extends State<T>
with
HzyScaffolMixin, // 脚手架混入
HzyAppBarMixin, // 导航栏混入
HzyBodyMixin, // 主体内容混入
HzyAbsAttribute, // 抽象属性混入
HzyAbstractNetWork, // 网络请求混入
HzyNormalLifeCycleAbs,// 生命周期混入
WidgetsBindingObserver // 应用生命周期观察者
{
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
configWidgetRenderingCompleted();
});
configDefault();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
Widget build(BuildContext context) {
return createScaffolWidget(context: context);
}
}
核心特性
- 原生状态管理:使用 Flutter 原生的
setState
进行状态管理 - 统一页面结构:提供标准的 Scaffold、AppBar、Body 结构
- 完整生命周期:集成页面和应用生命周期管理
- 缺省页面支持:内置加载、空数据、错误页面
- 安全区域处理:自动处理刘海屏适配
- 网络请求集成:内置网络请求状态管理
使用示例
创建一个简单的原生状态管理页面:
dart
// lib/pages/demo_page.dart
import 'package:flutter/material.dart';
import 'package:your_project/common/base_ui/base_normal_ui/base_state.dart';
class DemoPage extends StatefulWidget {
const DemoPage({Key? key}) : super(key: key);
@override
State<DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends BaseStateV<DemoPage> {
int count = 0;
String message = 'Hello World';
@override
void configDefault() {
super.configDefault();
// 初始化页面数据
loadData();
}
@override
String? createAppBarTitle() => '演示页面';
@override
bool get isShowAppBar => true;
@override
Widget createBody(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'计数: $count',
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 20),
Text(
message,
style: Theme.of(context).textTheme.bodyLarge,
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: increment,
child: const Text('增加'),
),
],
),
);
}
void increment() {
setState(() {
count++;
});
}
Future<void> loadData() async {
setState(() {
pageState = PageState.loadingState;
});
try {
// 模拟网络请求
await Future.delayed(const Duration(seconds: 2));
setState(() {
pageState = PageState.dataFetchState;
message = '数据加载成功';
});
} catch (e) {
setState(() {
pageState = PageState.errorState;
errMsg = '网络请求失败';
});
}
}
@override
void tapPlaceHoldWidgetMethod({required CommonPlaceHoldType placeHoldType}) {
if (placeHoldType == CommonPlaceHoldType.errorData) {
loadData();
}
}
}
缺省页面配置
BaseStateV
支持多种页面状态的缺省页面,您可以通过重写相应方法来自定义:
dart
class DemoPage extends BaseStateV<DemoPage> {
// 自定义空数据页面
@override
Widget? createEmptyWidget() {
return const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.inbox, size: 64, color: Colors.grey),
SizedBox(height: 16),
Text('暂无数据'),
],
),
);
}
// 自定义加载页面
@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),
Text(errMsg ?? '加载失败'),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () => loadData(),
child: const Text('重试'),
),
],
),
);
}
}
页面配置选项
您可以通过重写以下方法来配置页面的各种属性:
dart
class DemoPage extends BaseStateV<DemoPage> {
// 页面标题
@override
String? createAppBarTitle() => '我的页面';
// 是否显示导航栏
@override
bool get isShowAppBar => true;
// 背景颜色
@override
Color? get scallBackGroundColor => Colors.white;
// 是否使用安全区域
@override
bool get isNeedSafe => true;
// 导航栏操作按钮
@override
List<Widget>? createAppBarActions() => [
IconButton(
icon: const Icon(Icons.settings),
onPressed: () => Navigator.pushNamed(context, '/settings'),
),
];
// 是否显示返回按钮
@override
bool get isShowLeading => true;
}
BaseListStateV 列表基类
BaseListStateV
是专门为列表页面设计的基类,在 BaseStateV
的基础上集成了下拉刷新和上拉加载功能。
核心特性
- 下拉刷新:支持自定义刷新样式和逻辑
- 上拉加载:支持分页加载更多数据
- 状态管理:自动管理列表的加载、刷新、错误状态
- 空数据处理:内置空列表页面
- 网络请求:集成网络请求状态管理
使用示例
创建一个带有刷新功能的列表页面:
dart
// lib/pages/user_list_page.dart
import 'package:flutter/material.dart';
import 'package:your_project/common/base_ui/base_normal_ui/base_list_state.dart';
class UserListPage extends StatefulWidget {
const UserListPage({Key? key}) : super(key: key);
@override
State<UserListPage> createState() => _UserListPageState();
}
class _UserListPageState extends BaseListStateV<UserListPage> {
List<User> userList = [];
int currentPage = 1;
@override
void configDefault() {
super.configDefault();
loadData();
}
@override
String? createAppBarTitle() => '用户列表';
@override
Widget createRefreshChild() {
return ListView.builder(
itemCount: userList.length,
itemBuilder: (context, index) {
final user = userList[index];
return ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(user.avatar),
),
title: Text(user.name),
subtitle: Text(user.email),
onTap: () => _onUserTap(user),
);
},
);
}
@override
Future<void> configRefresh() async {
currentPage = 1;
await loadData();
}
@override
Future<void> configLoading() async {
currentPage++;
await loadData();
}
Future<void> loadData() async {
try {
setState(() {
if (currentPage == 1) {
pageState = PageState.loadingState;
}
});
// 模拟网络请求
final response = await UserApi.getUserList(
page: currentPage,
pageSize: 20,
);
setState(() {
if (currentPage == 1) {
userList = response.data;
} else {
userList.addAll(response.data);
}
// 设置页面状态
if (userList.isEmpty) {
pageState = PageState.emptyDataState;
} else {
pageState = PageState.dataFetchState;
}
// 设置加载更多状态
if (response.hasMore) {
configEndRefresh(IndicatorResult.success);
} else {
configEndRefresh(IndicatorResult.noMore);
}
});
} catch (e) {
setState(() {
pageState = PageState.errorState;
errMsg = e.toString();
configEndRefresh(IndicatorResult.fail);
});
}
}
void _onUserTap(User user) {
Navigator.pushNamed(
context,
'/user_detail',
arguments: user.id,
);
}
@override
void tapPlaceHoldWidgetMethod({required CommonPlaceHoldType placeHoldType}) {
if (placeHoldType == CommonPlaceHoldType.errorData) {
loadData();
}
}
}
class User {
final String id;
final String name;
final String email;
final String avatar;
User({
required this.id,
required this.name,
required this.email,
required this.avatar,
});
}
自定义刷新组件
您可以自定义下拉刷新和上拉加载的样式:
dart
class CustomListPage extends BaseListStateV<CustomListPage> {
@override
Widget createHeader() {
return const ClassicHeader(
refreshText: '下拉刷新',
refreshingText: '正在刷新...',
refreshedText: '刷新完成',
releaseText: '释放刷新',
);
}
@override
Widget createFooter() {
return const ClassicFooter(
loadText: '上拉加载',
loadingText: '正在加载...',
loadedText: '加载完成',
noMoreText: '没有更多数据',
);
}
}
列表状态管理
BaseListStateV
支持以下列表状态:
- 初始状态:页面首次加载
- 加载中:正在获取数据
- 有数据:成功获取到数据
- 空数据:没有数据时显示空页面
- 错误状态:网络请求失败
- 刷新中:下拉刷新进行中
- 加载更多:上拉加载更多数据
- 没有更多:所有数据已加载完成
与 GetX 方案对比
特性 | base_normal_ui | base_getx_ui |
---|---|---|
状态管理 | Flutter 原生 setState | GetX 响应式状态管理 |
代码复杂度 | 中等,需要手动管理状态 | 较低,自动响应式更新 |
学习成本 | 低(仅需了解 Flutter 基础) | 中(需要学习 GetX 概念) |
性能 | 优秀(直接使用原生机制) | 良好(GetX 内部优化) |
内存占用 | 较低 | 中等 |
适用场景 | 中小型项目、Flutter 初学者 | 大型项目、复杂状态管理 |
依赖 | 无第三方依赖 | 依赖 GetX 包 |
选择建议
选择 base_normal_ui 当:
- 项目规模较小
- 团队对 Flutter 原生开发更熟悉
- 不希望引入额外依赖
- 状态管理相对简单
选择 base_getx_ui 当:
- 项目规模较大
- 需要复杂的状态管理
- 团队熟悉响应式编程
- 希望减少样板代码
最佳实践
状态管理建议
dart
class BestPracticePage extends BaseStateV<BestPracticePage> {
// 1. 合理使用状态变量
bool _isLoading = false;
String? _errorMessage;
List<DataModel> _dataList = [];
// 2. 封装状态更新方法
void _updateLoadingState(bool loading) {
if (_isLoading != loading) {
setState(() {
_isLoading = loading;
});
}
}
// 3. 统一错误处理
void _handleError(dynamic error) {
setState(() {
_isLoading = false;
_errorMessage = error.toString();
pageState = PageState.errorState;
});
}
// 4. 数据加载最佳实践
Future<void> _loadData() async {
_updateLoadingState(true);
try {
final data = await ApiService.getData();
setState(() {
_dataList = data;
_isLoading = false;
_errorMessage = null;
pageState = data.isEmpty
? PageState.emptyDataState
: PageState.dataFetchState;
});
} catch (e) {
_handleError(e);
}
}
}
生命周期管理
dart
class LifecyclePage extends BaseStateV<LifecyclePage> {
Timer? _timer;
StreamSubscription? _subscription;
@override
void configDefault() {
super.configDefault();
_initializeResources();
}
@override
void dispose() {
// 清理资源
_timer?.cancel();
_subscription?.cancel();
super.dispose();
}
void _initializeResources() {
// 初始化定时器
_timer = Timer.periodic(Duration(seconds: 30), (timer) {
_refreshData();
});
// 监听数据流
_subscription = DataStream.listen((data) {
setState(() {
// 更新UI
});
});
}
}
错误处理
dart
class ErrorHandlingPage extends BaseStateV<ErrorHandlingPage> {
@override
Widget? createErrorWidget() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, size: 64, color: Colors.red),
SizedBox(height: 16),
Text(
errMsg ?? '加载失败',
style: TextStyle(fontSize: 16),
),
SizedBox(height: 16),
ElevatedButton(
onPressed: _retry,
child: Text('重试'),
),
],
),
);
}
void _retry() {
setState(() {
pageState = PageState.loadingState;
});
_loadData();
}
}
快速开始
使用 FluCli
提供的dart 代码块
快速创建基于原生状态管理的页面:
代码片段 | 描述 | 使用场景 |
---|---|---|
stLV | 自带上拉加载和下拉刷新功能的列表界面 | 需要分页加载数据的完整页面 |
stLW | 自带上拉加载和下拉刷新功能的列表组件 | 作为页面一部分的可复用列表组件 |
stV | 自带生命周期管理的有状态界面 | 需要管理状态的完整页面 |
stW | 自带生命周期管理的有状态组件 | 需要管理状态的可复用组件 |
leV | 无状态界面 | 纯展示、不需要状态管理的完整页面 |
leW | 无状态组件 | 纯展示、不需要状态管理的可复用组件 |
深入学习
- 想了解更多关于
GetX
的基础组件, 请参考 GetX UI 基础组件 。 - 想了解更多关于
代码块
的使用方法,请参考 Dart 代码片段速查手册 。