目录
- 创建flutter项目
- [国际化:l10n 带来的不仅是翻译](#国际化:l10n 带来的不仅是翻译 "#2-%E5%9B%BD%E9%99%85%E5%8C%96l10n-%E5%B8%A6%E6%9D%A5%E7%9A%84%E4%B8%8D%E4%BB%85%E6%98%AF%E7%BF%BB%E8%AF%91")
- [路由:为什么选 go_router?](#路由:为什么选 go_router? "#3-%E8%B7%AF%E7%94%B1%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89-go_router")
- [状态管理:Cubit + Bloc + Repository 分层模型](#状态管理:Cubit + Bloc + Repository 分层模型 "#4-%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86cubit--bloc--repository-%E5%88%86%E5%B1%82%E6%A8%A1%E5%9E%8B")
- 错误提示策略:SnackbarMessage
- [资源管理:flutter_gen 的价值](#资源管理:flutter_gen 的价值 "#6-%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86flutter_gen-%E7%9A%84%E4%BB%B7%E5%80%BC")
- 调试与可观测性
在开发一个 Flutter 应用时,通常需要考虑以下几个方面:
- 国际化(i18n):支持多语言,提升用户体验;
- 状态管理:管理页面之间或组件之间的数据状态;
- 资源引入:如图片、字体等静态资源的管理与使用;
- 路由管理:实现页面跳转和导航逻辑;
- 用户状态切换:处理未登录到已登录状态的转变;
- 错误提示机制:如登录失败时的错误反馈展示。
本文将以一个 Instagram 登录页为例,从 0 开始搭建一个 Flutter 项目,逐步实现上述功能,构建一个可持续开发的项目架构。本文完整代码:github.com/wutao23yzd/... 中的Demo5,效果如下:
1.创建flutter项目
创建flutter项目,采用very_good_cli,(pub.dev/packages/ve... 创建 Flutter 项目的好处在于,它提供了规范化的项目结构、严格的代码分析规则、内建测试和 CI 支持,帮助开发者快速搭建高质量、可维护的应用,特别适合团队协作和企业级项目开发。通过如下指令,可以创建一个指定组织名和包名的flutter应用
css
very_good create flutter_app flutter_instagram_clone_app --org "com.flutter" --application-id "com.flutter.futter_instagram_clone"
2. 国际化 (l10n)
l10n 是 "localization" 的缩写(l + 10个字母 + n),即"本地化/国际化"。very_good_cli创建好项目后,会自动生成国际化相关文件,但没有中文,可以这样子添加:
- 在 pubspec.yaml 中确保已经添加了 flutter_localizations 依赖。
- 在 l10n.yaml 配置文件中添加中文(zh)支持。
markdown
# 添加中文支持
preferred-supported-locales:
- en
- zh
- 在 arb 目录下添加中文的 ARB 文件(如 app_localizations_zh.arb),并翻译内容
- 添加新的文案时,使用flutter gen-l10n 重新生成本地化代码。
3. 路由:为什么选 go_router
路由使用go_routter (pub.dev/packages/go... 是 Flutter 官方推荐的路由管理库,,支持嵌套路由、参数传递、URL 同步、重定向和导航守卫等高级功能。相比传统的 Navigator,它结构更清晰、代码更简洁,而且支持基于用户登录状态的路由重定向逻辑,比如登录、登出,可以通过如下代码跳转:
ini
redirect: (context, state) {
final authenticated = appBloc.state.status == AppStatus.authenticated;
final authenticating = state.matchedLocation == AppRoutes.auth.route;
if (!authenticated) return AppRoutes.auth.route;
if (authenticating && authenticated) return AppRoutes.home.route;
return null;
}
4. 状态管理:Cubit + Bloc + Repository 分层模型
在 Flutter 项目中,采用 Cubit + Bloc + Repository 的分层模型 是一种清晰、可维护性强的架构设计。它将业务逻辑、状态管理和数据访问进行职责分离(pub.dev/packages/bl...
- Repository 层负责与数据源(如 API、数据库、缓存)交互,提供统一的数据获取接口。
- Bloc/Cubit 层负责管理状态和业务逻辑,从 Repository 获取数据并根据用户行为更新状态。
- **UI 层(Widget)**只关心状态展示,通过监听 Bloc/Cubit 提供的状态流进行响应式更新。
在提供的Demo中,AuthRepository 统一产出 用户身份流,任何需要身份信息的层(AppBloc)都只订阅这一个来源。
scala
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return RepositoryProvider(
// 全局唯一的数据层
create: (_) => AuthRepository(),
child: BlocProvider(
create: (context) => AppBloc(
authRepository: context.read<AuthRepository>(),
),
child: const AppView(),
),
);
}
}
- 在app.dart 顶层依赖注入,暴露一个Stream表示全局身份状态。
LoginCubit
只负责表单状态与调用AuthRepository.login
AppBloc
只订阅AuthRepository.user
,再映射成authenticated / unauthenticated
。
这样 UI ↔︎ 业务 ↔︎ 数据 的依赖方向清晰且单向。
5. 错误提示策略:SnackbarMessage 队列化
使用BlocListener<LoginCubit, LoginState>
监听当前状态,如果出现错误,则顶部弹出提示;使用clearIfQueue
清除旧消息 。
dart
BlocListener<LoginCubit, LoginState>(
listenWhen: (p, c) => p.status != c.status && c.status.isError,
listener: (_, s) => openSnackbar(
SnackbarMessage.error(title: '错误', description: s.message!),
clearIfQueue: true,
),
child: const LoginForm(),
);
6. 资源管理:flutter_gen 的价值
首先要要安装flutter_gen
依赖,同时在dev_dependencies
中,需要安装
makefile
build_runner: ^2.5.4
flutter_gen_runner: ^5.10.0
对于svg的文件,还需要安装依赖flutter_svg
;然后按照demo中所示,在pubspec.yaml
中提供资源所示路径;最后,执行dart run build_runner build
自动生成assets.gen.dart
文件,引用资源文件如下所示:
dart
Image.asset(Assets.images.logoPng.path);
FontFamily(Assets.fonts.montserrat); // 类型安全 + IDE 自动补全
- 不再担心路径拼写错误
- 对 Lottie / SVG 同样适用
7. 调试与可观测性
dart
class AppBlocObserver extends BlocObserver {
@override
void onChange(BlocBase bloc, Change change) =>
log('[Bloc] ${bloc.runtimeType} $change');
}
- 实时跟踪 Bloc / Cubit 状态变迁
- Flutter DevTools:开启 "Enhance tracing for user widgets"
写在最后
Demo大量代码使用了www.youtube.com/watch?v=xr5...