12 Flutter 实战项目与最佳实践

本章从架构设计模式出发,涵盖大型项目结构、多端适配、插件生态、无障碍与可访问性、代码规范与质量保障,是 Flutter 工程化的综合实践指南。


📋 章节目录

主题 核心内容
12.1 架构设计模式 MVC、MVVM、Clean Architecture
12.2 大型项目结构实践 分层架构、依赖注入、Either 模式
12.3 多端统一与适配 响应式布局、平台适配、导航适配
12.4 插件生态与工具链 常用库精选、build_runner、Melos
12.5 无障碍与可访问性 Semantics、对比度、字体缩放
12.6 代码规范与质量保障 analysis_options、pre-commit、质量门禁

12.1 架构设计模式

良好的架构设计是大型 Flutter 项目可维护性的保证。本节介绍三种主流架构模式及其在 Flutter 中的实践。

一、MVC(Model-View-Controller)

复制代码
Model:数据层(User、Product 等数据类)
View:Widget 层(UI 展示)
Controller:逻辑层(处理用户输入、调用 Model)
dart 复制代码
// Model
class UserModel {
  final String name;
  final String email;
  const UserModel({required this.name, required this.email});
}

// Controller
class UserController extends GetxController {
  final Rx<UserModel?> user = Rx<UserModel?>(null);
  final RxBool isLoading = false.obs;

  Future<void> loadUser(int id) async {
    isLoading.value = true;
    try {
      user.value = await UserRepository.findById(id);
    } finally {
      isLoading.value = false;
    }
  }
}

// View(Widget)
class UserView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final controller = Get.find<UserController>();
    return Obx(() =>
      controller.isLoading.value
          ? const CircularProgressIndicator()
          : UserCard(user: controller.user.value!),
    );
  }
}

二、MVVM(Model-View-ViewModel)

复制代码
Model:数据与业务规则
ViewModel:持有 UI 状态,处理逻辑
View:观察 ViewModel 状态,自动更新
dart 复制代码
// ViewModel(暴露 UI 状态)
class ProductListViewModel extends ChangeNotifier {
  List<Product> _products = [];
  bool _isLoading = false;
  String? _errorMessage;
  String _searchQuery = '';

  List<Product> get products => _filteredProducts;
  bool get isLoading => _isLoading;
  String? get errorMessage => _errorMessage;
  bool get isEmpty => _products.isEmpty && !_isLoading;

  List<Product> get _filteredProducts => _searchQuery.isEmpty
      ? _products
      : _products.where((p) => p.name.contains(_searchQuery)).toList();

  Future<void> loadProducts() async {
    _isLoading = true;
    _errorMessage = null;
    notifyListeners();

    try {
      _products = await ProductRepository.fetchAll();
    } catch (e) {
      _errorMessage = e.toString();
    } finally {
      _isLoading = false;
      notifyListeners();
    }
  }

  void search(String query) {
    _searchQuery = query;
    notifyListeners();
  }
}

// View(只关心 UI 展示)
class ProductListPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => ProductListViewModel()..loadProducts(),
      child: Consumer<ProductListViewModel>(
        builder: (context, vm, _) {
          if (vm.isLoading) return const LoadingWidget();
          if (vm.errorMessage != null) return ErrorWidget(message: vm.errorMessage!);
          if (vm.isEmpty) return const EmptyState();
          return ProductGrid(products: vm.products);
        },
      ),
    );
  }
}

三、Clean Architecture(推荐大型项目)

复制代码
┌────────────────────────────────────────┐
│  Presentation Layer(UI + ViewModel)   │
│  Widget / Page / Controller / Provider │
├────────────────────────────────────────┤
│  Domain Layer(业务规则)               │
│  Entity / UseCase / Repository接口     │
├────────────────────────────────────────┤
│  Data Layer(数据来源)                 │
│  Repository实现 / DataSource / Model   │
└────────────────────────────────────────┘
dart 复制代码
// =============== Domain Layer ===============

// Entity(纯业务对象)
class Product {
  final int id;
  final String name;
  final double price;
  const Product({required this.id, required this.name, required this.price});
}

// Repository 接口(抽象,不关心实现)
abstract class ProductRepository {
  Future<List<Product>> getProducts({String? category, int page = 1});
  Future<Product> getProductById(int id);
  Future<void> toggleFavorite(int productId);
}

// UseCase(单一业务用例,可独立测试)
class GetProductsUseCase {
  final ProductRepository repository;
  GetProductsUseCase(this.repository);

  Future<List<Product>> call({String? category, int page = 1}) async {
    final products = await repository.getProducts(category: category, page: page);
    return products.where((p) => p.isAvailable).toList();
  }
}

// =============== Data Layer ===============

class ProductDto {
  final int id;
  final String name;
  final double price;
  final bool isActive;

  const ProductDto({required this.id, required this.name, required this.price, required this.isActive});

  factory ProductDto.fromJson(Map<String, dynamic> json) => ProductDto(
    id: json['id'],
    name: json['name'],
    price: json['price'].toDouble(),
    isActive: json['is_active'],
  );

  Product toDomain() => Product(id: id, name: name, price: price);
}

class ProductRepositoryImpl implements ProductRepository {
  final ProductRemoteDataSource _remote;
  final ProductLocalDataSource _local;

  ProductRepositoryImpl(this._remote, this._local);

  @override
  Future<List<Product>> getProducts({String? category, int page = 1}) async {
    try {
      final dtos = await _remote.fetchProducts(category: category, page: page);
      await _local.cacheProducts(dtos);
      return dtos.map((dto) => dto.toDomain()).toList();
    } catch (_) {
      final cached = await _local.getCachedProducts();
      return cached.map((dto) => dto.toDomain()).toList();
    }
  }
}

// =============== Presentation Layer ===============

@riverpod
Future<List<Product>> productList(ProductListRef ref, {String? category}) async {
  final useCase = ref.watch(getProductsUseCaseProvider);
  return useCase(category: category);
}

class ProductListPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final productsAsync = ref.watch(productListProvider());
    return productsAsync.when(
      loading: () => const ProductGridSkeleton(),
      error: (e, _) => ErrorView(error: e),
      data: (products) => ProductGrid(products: products),
    );
  }
}

小结

架构 适用规模 特点
MVC 小型项目 简单,快速上手,适合 GetX
MVVM 中型项目 ViewModel 与 View 解耦,可测试
Clean Architecture 大型项目 分层清晰,高可测试性,团队协作友好

12.2 大型项目结构实践

大型 Flutter 项目需要清晰的分层架构、完善的依赖注入体系和统一的编码规范,才能支撑多人长期协作。

一、分层架构(data / domain / ui)

复制代码
lib/
├── core/                     # 核心基础设施(跨所有功能共享)
│   ├── network/
│   │   ├── api_client.dart
│   │   ├── interceptors/
│   │   └── exceptions/
│   ├── storage/
│   ├── utils/
│   ├── extensions/
│   └── constants/
│
├── features/                 # 功能模块(按业务域划分)
│   ├── auth/
│   │   ├── data/
│   │   │   ├── datasources/
│   │   │   │   ├── auth_remote_datasource.dart
│   │   │   │   └── auth_local_datasource.dart
│   │   │   ├── models/
│   │   │   │   └── user_dto.dart
│   │   │   └── repositories/
│   │   │       └── auth_repository_impl.dart
│   │   ├── domain/
│   │   │   ├── entities/
│   │   │   │   └── user.dart
│   │   │   ├── repositories/
│   │   │   │   └── auth_repository.dart
│   │   │   └── usecases/
│   │   │       ├── login_usecase.dart
│   │   │       └── logout_usecase.dart
│   │   └── presentation/
│   │       ├── providers/
│   │       │   └── auth_provider.dart
│   │       ├── pages/
│   │       │   └── login_page.dart
│   │       └── widgets/
│   │           └── login_form.dart
│   │
│   ├── product/
│   └── order/
│
├── shared/                    # 跨功能共享组件
│   ├── widgets/
│   ├── theme/
│   └── router/
│
└── main.dart

二、依赖注入与全局单例

2.1 Riverpod + 分层 Provider
dart 复制代码
// core/network/providers.dart
@riverpod
DioClient dioClient(DioClientRef ref) => DioClient();

// features/auth/data/providers.dart
@riverpod
AuthRemoteDataSource authRemoteDataSource(AuthRemoteDataSourceRef ref) {
  return AuthRemoteDataSourceImpl(ref.watch(dioClientProvider));
}

@riverpod
AuthRepository authRepository(AuthRepositoryRef ref) {
  return AuthRepositoryImpl(
    remote: ref.watch(authRemoteDataSourceProvider),
    local: ref.watch(authLocalDataSourceProvider),
  );
}

// features/auth/domain/providers.dart
@riverpod
LoginUseCase loginUseCase(LoginUseCaseRef ref) {
  return LoginUseCase(ref.watch(authRepositoryProvider));
}
2.2 GetIt(服务定位器)
dart 复制代码
final getIt = GetIt.instance;

Future<void> setupDependencies() async {
  getIt.registerSingleton<DioClient>(DioClient());
  getIt.registerSingleton<DatabaseService>(await DatabaseService.init());

  getIt.registerLazySingleton<ProductRemoteDataSource>(
    () => ProductRemoteDataSourceImpl(getIt<DioClient>()),
  );

  getIt.registerLazySingleton<ProductRepository>(
    () => ProductRepositoryImpl(
      remote: getIt<ProductRemoteDataSource>(),
      local: getIt<ProductLocalDataSource>(),
    ),
  );

  getIt.registerFactory<GetProductsUseCase>(
    () => GetProductsUseCase(getIt<ProductRepository>()),
  );
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await setupDependencies();
  runApp(const MyApp());
}

三、统一的响应/错误处理

dart 复制代码
import 'package:fpdart/fpdart.dart';

abstract class Failure {
  final String message;
  const Failure(this.message);
}

class NetworkFailure extends Failure {
  const NetworkFailure(super.message);
}

class CacheFailure extends Failure {
  const CacheFailure(super.message);
}

// Repository 返回 Either<Failure, T>
abstract class ProductRepository {
  Future<Either<Failure, List<Product>>> getProducts();
}

// UI 处理
final result = await useCase();
result.fold(
  (failure) => showError(failure.message), // Left = 失败
  (products) => showProducts(products),     // Right = 成功
);

四、编码规范

dart 复制代码
// ✅ 文件命名:snake_case
// product_detail_page.dart

// ✅ 类命名:PascalCase
class ProductDetailPage {}

// ✅ 变量/方法命名:camelCase
final currentUser = UserModel();
Future<void> loadUserData() async {}

// ✅ 常量:lowerCamelCase
const maxRetryCount = 3;
const String apiBaseUrl = 'https://api.example.com';

// ✅ 私有成员:下划线前缀
final _controller = TextEditingController();
void _handleSubmit() {}

// ✅ Barrel exports
// features/product/product.dart
export 'presentation/pages/product_list_page.dart';
export 'presentation/pages/product_detail_page.dart';
export 'domain/entities/product.dart';

小结

实践 要点
分层 data / domain / presentation 三层分离
功能模块 按业务域组织,每个模块独立完整
依赖注入 Riverpod/GetIt 管理依赖,避免全局单例硬编码
Either 模式 函数式错误处理,避免 try-catch 满天飞
命名规范 统一 snake_case 文件名,PascalCase 类名

12.3 多端统一与适配

Flutter 的跨端能力是其核心优势。合理的设计能让同一套代码在手机、平板、桌面、Web 上都有良好的体验。

一、响应式布局与屏幕适配

1.1 flutter_screenutil
yaml 复制代码
dependencies:
  flutter_screenutil: ^5.9.0
dart 复制代码
void main() {
  runApp(
    ScreenUtilInit(
      designSize: const Size(375, 812), // 设计稿尺寸(以 iPhone X 为基准)
      minTextAdapt: true,
      splitScreenMode: true,
      builder: (context, child) => MaterialApp(
        theme: AppTheme.lightTheme,
        home: child,
        routes: routes,
      ),
      child: const HomePage(),
    ),
  );
}

Widget build(BuildContext context) {
  return Container(
    width: 100.w,   // 自动适配宽度
    height: 200.h,  // 自动适配高度
    padding: EdgeInsets.all(16.r),
    child: Text(
      '标题',
      style: TextStyle(fontSize: 18.sp),
    ),
  );
}
1.2 自定义响应式断点
dart 复制代码
class Breakpoints {
  static const double compact = 600;
  static const double medium = 1200;

  static bool isCompact(BuildContext context) =>
      MediaQuery.of(context).size.width < compact;

  static bool isMedium(BuildContext context) {
    final w = MediaQuery.of(context).size.width;
    return w >= compact && w < medium;
  }

  static bool isExpanded(BuildContext context) =>
      MediaQuery.of(context).size.width >= medium;
}

class ResponsiveLayout extends StatelessWidget {
  final Widget compact;
  final Widget? medium;
  final Widget? expanded;

  const ResponsiveLayout({
    super.key,
    required this.compact,
    this.medium,
    this.expanded,
  });

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth >= Breakpoints.medium) {
          return expanded ?? medium ?? compact;
        }
        if (constraints.maxWidth >= Breakpoints.compact) {
          return medium ?? compact;
        }
        return compact;
      },
    );
  }
}

// 使用
ResponsiveLayout(
  compact: const MobileLayout(),
  medium: const TabletLayout(),
  expanded: const DesktopLayout(),
)

二、平台自适应组件

dart 复制代码
class AdaptiveSwitch extends StatelessWidget {
  final bool value;
  final ValueChanged<bool>? onChanged;

  const AdaptiveSwitch({super.key, required this.value, this.onChanged});

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return switch (theme.platform) {
      TargetPlatform.iOS || TargetPlatform.macOS =>
          CupertinoSwitch(value: value, onChanged: onChanged),
      _ => Switch(value: value, onChanged: onChanged),
    };
  }
}

三、多语言与多主题支持

dart 复制代码
class MyApp extends ConsumerWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final locale = ref.watch(localeProvider);
    final themeMode = ref.watch(themeModeProvider);

    return MaterialApp.router(
      routerConfig: router,
      theme: AppTheme.lightTheme,
      darkTheme: AppTheme.darkTheme,
      themeMode: themeMode,
      locale: locale,
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      debugShowCheckedModeBanner: false,
    );
  }
}

四、不同屏幕尺寸的导航适配

dart 复制代码
class AdaptiveNavigationScaffold extends StatelessWidget {
  final int selectedIndex;
  final ValueChanged<int> onDestinationSelected;
  final Widget body;

  const AdaptiveNavigationScaffold({
    super.key,
    required this.selectedIndex,
    required this.onDestinationSelected,
    required this.body,
  });

  static const destinations = [
    NavigationDestination(icon: Icon(Icons.home), label: '首页'),
    NavigationDestination(icon: Icon(Icons.explore), label: '发现'),
    NavigationDestination(icon: Icon(Icons.person), label: '我的'),
  ];

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth >= 1200) {
          // 桌面:永久侧边抽屉
          return Scaffold(
            body: Row(
              children: [
                NavigationDrawer(
                  selectedIndex: selectedIndex,
                  onDestinationSelected: onDestinationSelected,
                  children: destinations
                      .map((d) => NavigationDrawerDestination(
                            icon: d.icon,
                            label: Text(d.label),
                          ))
                      .toList(),
                ),
                Expanded(child: body),
              ],
            ),
          );
        } else if (constraints.maxWidth >= 600) {
          // 平板:侧边 NavigationRail
          return Scaffold(
            body: Row(
              children: [
                NavigationRail(
                  selectedIndex: selectedIndex,
                  onDestinationSelected: onDestinationSelected,
                  labelType: NavigationRailLabelType.all,
                  destinations: destinations
                      .map((d) => NavigationRailDestination(
                            icon: d.icon,
                            label: Text(d.label),
                          ))
                      .toList(),
                ),
                const VerticalDivider(thickness: 1, width: 1),
                Expanded(child: body),
              ],
            ),
          );
        } else {
          // 手机:底部导航栏
          return Scaffold(
            body: body,
            bottomNavigationBar: NavigationBar(
              selectedIndex: selectedIndex,
              onDestinationSelected: onDestinationSelected,
              destinations: destinations,
            ),
          );
        }
      },
    );
  }
}

小结

适配维度 工具/方案
尺寸自适应 flutter_screenutilLayoutBuilder
响应式布局 断点系统、ResponsiveLayout 组件
平台风格 AdaptiveSwitchPlatform.isIOS
导航适配 手机→底部栏,平板→Rail,桌面→Drawer
多主题 ThemeModeThemeExtension
多语言 flutter_localizations、动态切换

12.4 插件生态与工具链

Flutter 拥有活跃的插件生态。了解常用第三方库和开发工具链,能大幅提升开发效率。

一、常用第三方库精选

1.1 网络与数据
功能 推荐度
dio HTTP 客户端,拦截器,文件上传 ⭐⭐⭐⭐⭐
retrofit 基于 Dio 的类型安全 REST 客户端 ⭐⭐⭐⭐
json_serializable JSON 代码生成 ⭐⭐⭐⭐⭐
freezed 不可变数据类 + Union 类型 ⭐⭐⭐⭐⭐
1.2 状态管理
功能 推荐度
flutter_riverpod 现代状态管理(推荐) ⭐⭐⭐⭐⭐
provider 官方推荐,轻量 ⭐⭐⭐⭐
flutter_bloc 事件驱动,大型项目 ⭐⭐⭐⭐
get 全功能一体化框架 ⭐⭐⭐
1.3 路由
功能 推荐度
go_router 官方团队,声明式路由 ⭐⭐⭐⭐⭐
auto_route 代码生成,类型安全 ⭐⭐⭐⭐
1.4 本地存储
功能 推荐度
shared_preferences 键值对存储 ⭐⭐⭐⭐⭐
hive_flutter 高性能 NoSQL ⭐⭐⭐⭐
isar 现代 NoSQL + 全文搜索 ⭐⭐⭐⭐
sqflite SQLite 关系型数据库 ⭐⭐⭐
1.5 UI 与媒体
功能 推荐度
cached_network_image 网络图片缓存 ⭐⭐⭐⭐⭐
flutter_svg SVG 渲染 ⭐⭐⭐⭐⭐
lottie Lottie 动画 ⭐⭐⭐⭐⭐
rive Rive 交互动画 ⭐⭐⭐⭐
flutter_screenutil 屏幕尺寸适配 ⭐⭐⭐⭐
shimmer 骨架屏占位动画 ⭐⭐⭐⭐
1.6 工具与调试
功能 推荐度
logger 分级日志输出 ⭐⭐⭐⭐⭐
sentry_flutter 错误上报 ⭐⭐⭐⭐⭐
permission_handler 权限申请 ⭐⭐⭐⭐⭐
url_launcher 打开 URL/邮件/电话 ⭐⭐⭐⭐⭐
package_info_plus 获取 App 版本信息 ⭐⭐⭐⭐
device_info_plus 获取设备信息 ⭐⭐⭐⭐

二、代码生成工具

bash 复制代码
# 一次性生成
dart run build_runner build --delete-conflicting-outputs

# 监听文件变化,自动重新生成(开发时推荐)
dart run build_runner watch --delete-conflicting-outputs

触发代码生成的场景:

  • json_serializable → 生成 *.g.dart
  • freezed → 生成 *.freezed.dart + *.g.dart
  • riverpod_generator → 生成 *.g.dart
  • go_router_builder → 生成路由代码
  • mockito → 生成 *.mocks.dart
makefile 复制代码
# Makefile
.PHONY: gen clean test build

gen:
	dart run build_runner build --delete-conflicting-outputs

gen-watch:
	dart run build_runner watch --delete-conflicting-outputs

clean:
	flutter clean && flutter pub get

test:
	flutter test --coverage

format:
	dart format . --line-length=100

lint:
	flutter analyze

build-android:
	flutter build appbundle --release --dart-define-from-file=.env.prod

build-ios:
	flutter build ipa --release --dart-define-from-file=.env.prod

三、Melos 自动化工具

bash 复制代码
# 安装 Melos
dart pub global activate melos

melos bootstrap    # 引导所有包
melos run gen      # 代码生成
melos run test     # 全量测试
melos run lint     # 代码检查

四、IDE 支持

VS Code 推荐设置(settings.json):

json 复制代码
{
  "editor.formatOnSave": true,
  "editor.rulers": [100],
  "[dart]": {
    "editor.defaultFormatter": "Dart-Code.dart-code",
    "editor.tabSize": 2
  },
  "dart.lineLength": 100,
  "dart.hotReloadOnSave": "always",
  "dart.previewFlutterUiGuides": true
}

analysis_options.yaml(代码质量):

yaml 复制代码
include: package:flutter_lints/flutter.yaml

analyzer:
  errors:
    missing_required_param: error
    missing_return: error
  exclude:
    - '**/*.g.dart'
    - '**/*.freezed.dart'

linter:
  rules:
    - always_declare_return_types
    - annotate_overrides
    - avoid_empty_else
    - avoid_print
    - prefer_const_constructors
    - prefer_const_declarations
    - prefer_final_fields
    - sized_box_for_whitespace
    - use_key_in_widget_constructors

小结

类别 推荐栈
HTTP Dio + Retrofit
状态管理 Riverpod + Freezed
路由 GoRouter
本地存储 SharedPreferences + Isar
代码生成 build_runner + json_serializable + freezed
Monorepo Melos
错误上报 Sentry
日志 Logger

12.5 无障碍与可访问性(Accessibility)

无障碍设计确保视障、听障和行动不便的用户也能使用你的 App。Flutter 通过 Semantics 树与平台辅助功能(TalkBack / VoiceOver)集成,实现屏幕阅读器支持。

一、Semantics 基础

dart 复制代码
// 手动添加语义信息
Semantics(
  label: '购物车图标,当前有 3 件商品',
  button: true,
  child: IconButton(
    icon: const Icon(Icons.shopping_cart),
    onPressed: _goToCart,
  ),
)

// 图片需要手动添加语义描述
Image.network(
  product.imageUrl,
  semanticLabel: '${product.name} 商品图片',
)

// 排除不需要的语义(纯装饰性元素)
ExcludeSemantics(
  child: Image.asset('assets/images/decorative_divider.png'),
)

二、常用 Semantics 属性

dart 复制代码
Semantics(
  // 基础标签
  label: '提交订单按钮',       // 屏幕阅读器朗读的文字
  hint: '双击以提交订单',      // 操作提示
  value: '¥299.00',          // 当前值

  // 状态
  enabled: true,
  selected: isActive,
  checked: isChecked,
  toggled: isOn,

  // 角色
  button: true,
  header: true,
  link: true,
  image: true,
  textField: true,

  // 操作
  onTap: _handleTap,
  onLongPress: _handleLongPress,

  child: child,
)

三、常见场景适配

dart 复制代码
// ❌ GestureDetector 没有语义信息
GestureDetector(
  onTap: _addToCart,
  child: Container(child: const Text('加入购物车')),
)

// ✅ 添加 Semantics
Semantics(
  label: '加入购物车',
  button: true,
  child: GestureDetector(
    onTap: _addToCart,
    child: Container(child: const Text('加入购物车')),
  ),
)

// 列表项
Semantics(
  label: '${product.name},价格 ${product.price} 元,${product.inStock ? "有货" : "缺货"}',
  child: ProductCard(product: product),
)

// 分组语义(MergeSemantics 合并子节点)
MergeSemantics(
  child: ListTile(
    leading: const Icon(Icons.notifications),
    title: const Text('推送通知'),
    trailing: Switch(
      value: _pushEnabled,
      onChanged: (v) => setState(() => _pushEnabled = v),
    ),
  ),
)
// 屏幕阅读器朗读:"推送通知,开关,已开启"

四、对比度与字体缩放

dart 复制代码
// WCAG AA 标准:文字与背景对比度 ≥ 4.5:1
// ❌ 低对比度
Text('重要提示', style: TextStyle(color: Color(0xFFBBBBBB)))

// ✅ 高对比度
Text('重要提示', style: TextStyle(color: Color(0xFF333333)))

// 支持系统字体缩放
MaterialApp(
  builder: (context, child) {
    return MediaQuery(
      data: MediaQuery.of(context).copyWith(
        textScaler: MediaQuery.of(context).textScaler.clamp(
          minScaleFactor: 0.8,
          maxScaleFactor: 1.5,
        ),
      ),
      child: child!,
    );
  },
)

五、测试无障碍

dart 复制代码
// 开启语义调试器(开发时使用)
MaterialApp(
  showSemanticsDebugger: true,
  home: const MyApp(),
)

// Widget 测试中检查语义
testWidgets('产品卡片包含正确的语义标签', (tester) async {
  await tester.pumpWidget(
    MaterialApp(home: ProductCard(product: testProduct)),
  );

  expect(
    find.bySemanticsLabel('Flutter 入门教程,价格 99 元'),
    findsOneWidget,
  );
});

六、无障碍检查清单

检查项 要求
所有可交互元素有语义标签 Semantics(label: ...) 或内置 Widget
图片有替代文字 semanticLabelExcludeSemantics(装饰图)
足够的颜色对比度 ≥ 4.5:1(WCAG AA)
支持字体缩放 适配 textScaler,保证布局不溢出
最小触摸区域 48×48 dp Material 规范最低要求
焦点顺序合理 Tab 键遍历顺序自然
状态变化有反馈 切换按钮朗读新状态

12.6 代码规范与质量保障

统一的代码规范是团队协作的基础。Flutter 项目通过 analysis_options、pre-commit hooks、代码审查流程和自动化质量门禁,保障代码质量。

一、analysis_options.yaml 完整配置

yaml 复制代码
include: package:flutter_lints/flutter.yaml

analyzer:
  strong-mode:
    implicit-casts: false
    implicit-dynamic: false

  language:
    strict-casts: true
    strict-inference: true
    strict-raw-types: true

  errors:
    missing_required_param: error
    missing_return: error
    dead_code: warning
    unused_import: warning
    unnecessary_null_comparison: warning

  exclude:
    - '**/*.g.dart'
    - '**/*.freezed.dart'
    - 'build/**'
    - 'lib/generated/**'

linter:
  rules:
    # ===== 代码风格 =====
    - always_declare_return_types
    - annotate_overrides
    - prefer_const_constructors
    - prefer_const_declarations
    - prefer_final_fields
    - prefer_final_locals
    - sort_child_properties_last
    - use_key_in_widget_constructors

    # ===== 正确性 =====
    - avoid_print
    - avoid_empty_else
    - cancel_subscriptions
    - close_sinks

    # ===== 性能 =====
    - sized_box_for_whitespace
    - avoid_unnecessary_containers
    - use_decorated_box

    # ===== 可读性 =====
    - prefer_single_quotes
    - directives_ordering
    - require_trailing_commas

二、pre-commit Hooks

yaml 复制代码
# lefthook.yml
pre-commit:
  parallel: true
  commands:
    dart-format:
      glob: "*.dart"
      run: dart format --set-exit-if-changed {staged_files}

    flutter-analyze:
      run: flutter analyze --no-pub

    tests:
      run: flutter test --no-pub

pre-push:
  commands:
    full-test:
      run: flutter test --coverage
bash 复制代码
brew install lefthook
lefthook install
lefthook run pre-commit  # 手动触发

三、PR 规范

markdown 复制代码
## PR 标题格式
feat(module): 添加订单列表分页功能
fix(auth): 修复 Token 刷新死循环
refactor(cart): 重构购物车状态管理为 Riverpod

## PR 描述模板(.github/pull_request_template.md)

## 变更说明
<!-- 简述本次变更的内容和目标 -->

## 变更类型
- [ ] 新功能(feat)
- [ ] Bug 修复(fix)
- [ ] 代码重构(refactor)
- [ ] 性能优化(perf)

## 测试
- [ ] 已添加单元测试
- [ ] 已在真机测试(iOS / Android)
- [ ] 已测试暗色模式

## 关联 Issue
Closes #123

四、日志分级规范

dart 复制代码
final logger = Logger(
  printer: kReleaseMode
      ? ProductionFilter()
      : PrettyPrinter(
          methodCount: 2,
          errorMethodCount: 8,
          lineLength: 120,
          colors: true,
          printEmojis: true,
          printTime: true,
        ),
  level: kReleaseMode ? Level.warning : Level.trace,
);

class ApiClient {
  void _logRequest(String url, Map<String, dynamic>? body) {
    logger.d('→ $url', extra: body);
  }

  void _logResponse(String url, int statusCode, dynamic data) {
    if (statusCode >= 400) {
      logger.w('← $url [$statusCode]', error: data);
    } else {
      logger.d('← $url [$statusCode]');
    }
  }

  void _logError(String url, Object error, StackTrace stack) {
    logger.e('✗ $url', error: error, stackTrace: stack);
  }
}

五、Melos 工作区脚本

yaml 复制代码
# melos.yaml
name: my_flutter_workspace

packages:
  - apps/*
  - packages/*

scripts:
  lint:
    exec: flutter analyze
    description: 分析所有包

  format:
    exec: dart format .
    description: 格式化所有 Dart 文件

  test:
    exec: flutter test --coverage
    description: 运行所有测试

  gen:
    exec: dart run build_runner build --delete-conflicting-outputs
    description: 运行代码生成

  prepare:
    run: melos bootstrap && melos run gen
    description: 初始化工作区
bash 复制代码
melos bootstrap    # 安装所有包的依赖
melos run lint     # 检查所有包
melos run test     # 测试所有包
melos run gen      # 代码生成(所有包)

六、质量门禁(Quality Gate)

yaml 复制代码
# .github/workflows/quality_gate.yml
name: Quality Gate

on: [pull_request]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: subosito/flutter-action@v2
        with:
          flutter-version: '3.19.6'
          cache: true

      - run: flutter pub get

      - name: 格式检查
        run: dart format --output=none --set-exit-if-changed .

      - name: 静态分析
        run: flutter analyze --fatal-warnings

      - name: 测试与覆盖率
        run: |
          flutter test --coverage
          total=$(lcov --summary coverage/lcov.info 2>&1 | grep "lines" | awk '{print $2}' | tr -d '%')
          if (( $(echo "$total < 70" | bc -l) )); then
            echo "❌ 测试覆盖率 $total% 低于要求的 70%"
            exit 1
          fi
          echo "✅ 测试覆盖率: $total%"

小结

工具/实践 目的
analysis_options.yaml 自动检测代码问题和风格
pre-commit hooks 提交前强制检查
PR 规范 统一 PR 格式,提高审查效率
日志分级 生产环境不输出调试日志
Melos 脚本 统一工作区命令
质量门禁 CI 自动化验证质量标准

章节总结

知识点 必掌握程度
Clean Architecture ⭐⭐⭐⭐⭐
分层目录结构 ⭐⭐⭐⭐⭐
Riverpod / GetIt 依赖注入 ⭐⭐⭐⭐⭐
响应式布局断点系统 ⭐⭐⭐⭐
导航适配(手机/平板/桌面) ⭐⭐⭐⭐
常用插件技术栈 ⭐⭐⭐⭐⭐
Semantics 无障碍 ⭐⭐⭐
质量门禁 + CI 流程 ⭐⭐⭐⭐⭐

🎉 恭喜!全部 12 章 Flutter 知识体系文档已完整整合。

从基础入门(第 1 章)到进阶原理(第 11 章),再到实战最佳实践(第 12 章),

构建了完整的 Flutter 学习路线图。

👉 返回 README 目录 查看完整文档导航

相关推荐
里欧跑得慢12 小时前
Flutter 测试全攻略:从单元测试到集成测试的完整实践
前端·css·flutter·web
键盘鼓手苏苏16 小时前
Flutter 三方库 pip 的鸿蒙化适配指南 - 实现标准化的画中画(Picture-in-Picture)模式、支持视频悬浮窗与多任务并行交互
flutter·pip·harmonyos
左手厨刀右手茼蒿16 小时前
Flutter 组件 sheety_localization 的适配 鸿蒙Harmony 实战 - 驾驭在线协作式多语言管理、实现鸿蒙端动态词条下发与全球化敏捷发布方案
flutter·harmonyos·鸿蒙·openharmony·sheety_localization
见山是山-见水是水17 小时前
鸿蒙flutter第三方库适配 - 路由书签应用
flutter·华为·harmonyos
火柴就是我17 小时前
记录一些跨平台开发需要的鸿蒙知识
flutter·harmonyos
Tong Z17 小时前
Flutter中的三种通道
flutter
空中海18 小时前
2.3 组件复用与组合
flutter·dart
你听得到1119 小时前
Get 这波之后,我把 Flutter 状态管理重新看了一遍:新项目到底该选谁?
前端·flutter·架构